@m3ui-vue/m3ui-vue 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/_MContextMenuPanel.vue.d.ts +1 -1
- package/dist/m3ui-vue.css +1 -1
- package/dist/m3ui.js +70 -68
- package/dist/m3ui.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/MTour.vue +57 -56
- package/src/components/_MContextMenuPanel.vue +47 -41
package/dist/m3ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"m3ui.js","names":["$slots","$slots","$slots","$emit","$slots","$slots","$slots","$slots","$slots","$emit","$slots","$slots","$emit","$slots","$slots","$slots","$slots","$slots","$slots","$slots","$slots","$emit","$slots","$slots","$slots","$slots"],"sources":["../src/plugin.ts","../src/composables/useTheme.ts","../src/composables/useColorPalette.ts","../src/composables/useToast.ts","../src/composables/useFieldBg.ts","../src/components/MAlert.vue","../src/components/MAlert.vue","../src/components/MAppBar.vue","../src/components/MAppBar.vue","../src/components/MAvatar.vue","../src/components/MAvatar.vue","../src/components/MBadge.vue","../src/components/MBadge.vue","../src/components/MBottomSheet.vue","../src/components/MBottomSheet.vue","../src/components/MBreadcrumbs.vue","../src/components/MBreadcrumbs.vue","../src/components/MSpinner.vue","../src/components/MSpinner.vue","../src/components/MButton.vue","../src/components/MButton.vue","../src/components/MIconButton.vue","../src/components/MIconButton.vue","../src/components/MCalendar.vue","../src/components/MCalendar.vue","../src/components/MCard.vue","../src/components/MCard.vue","../src/components/MCheckbox.vue","../src/components/MCheckbox.vue","../src/components/MChip.vue","../src/components/MChip.vue","../src/components/MColorPicker.vue","../src/components/MColorPicker.vue","../src/components/MCommandPalette.vue","../src/components/MCommandPalette.vue","../src/components/MDialog.vue","../src/components/MDialog.vue","../src/components/MConfirmDialog.vue","../src/components/MConfirmDialog.vue","../src/components/MContainer.vue","../src/components/MContainer.vue","../src/components/_MContextMenuPanel.vue","../src/components/_MContextMenuPanel.vue","../src/components/MContextMenu.vue","../src/components/MContextMenu.vue","../src/components/MPagination.vue","../src/components/MPagination.vue","../src/components/MDataTable.vue","../src/components/MDataTable.vue","../src/components/MDatePicker.vue","../src/components/MDatePicker.vue","../src/components/MDateRangePicker.vue","../src/components/MDateRangePicker.vue","../src/components/MDivider.vue","../src/components/MDivider.vue","../src/components/MDragDropList.vue","../src/components/MDragDropList.vue","../src/components/MEmptyState.vue","../src/components/MEmptyState.vue","../src/components/MExpansionPanel.vue","../src/components/MExpansionPanel.vue","../src/components/MFab.vue","../src/components/MFab.vue","../src/components/MFileUpload.vue","../src/components/MFileUpload.vue","../src/components/MGrid.vue","../src/components/MGrid.vue","../src/components/MHotkeys.vue","../src/components/MHotkeys.vue","../src/components/MInfiniteScroll.vue","../src/components/MInfiniteScroll.vue","../src/components/MJsonViewer.vue","../src/components/MJsonViewer.vue","../src/components/MKanban.vue","../src/components/MKanban.vue","../src/components/MLoadingOverlay.vue","../src/components/MLoadingOverlay.vue","../src/components/MMasonry.vue","../src/components/MMasonry.vue","../src/components/MMenu.vue","../src/components/MMenu.vue","../src/components/MMenuItem.vue","../src/components/MMenuItem.vue","../src/components/MMultiSelect.vue","../src/components/MMultiSelect.vue","../src/components/MNavigationBar.vue","../src/components/MNavigationBar.vue","../src/components/MNavigationDrawer.vue","../src/components/MNavigationDrawer.vue","../src/components/MNavigationRail.vue","../src/components/MNavigationRail.vue","../src/components/MProgressBar.vue","../src/components/MProgressBar.vue","../src/components/MRadio.vue","../src/components/MRadio.vue","../src/components/MRadioGroup.vue","../src/components/MRadioGroup.vue","../src/components/MRating.vue","../src/components/MRating.vue","../src/components/MResult.vue","../src/components/MResult.vue","../src/components/MScheduler.vue","../src/components/MScheduler.vue","../src/components/MSegmentedButton.vue","../src/components/MSegmentedButton.vue","../src/components/MSelect.vue","../src/components/MSelect.vue","../src/components/MSideSheet.vue","../src/components/MSideSheet.vue","../src/components/MSkeleton.vue","../src/components/MSkeleton.vue","../src/components/MSlider.vue","../src/components/MSlider.vue","../src/components/MSnackbar.vue","../src/components/MSnackbar.vue","../src/components/MSplitter.vue","../src/components/MSplitter.vue","../src/components/MSpotlightSearch.vue","../src/components/MSpotlightSearch.vue","../src/components/MStack.vue","../src/components/MStack.vue","../src/components/MStatCard.vue","../src/components/MStatCard.vue","../src/components/MStepper.vue","../src/components/MStepper.vue","../src/components/MSwitch.vue","../src/components/MSwitch.vue","../src/components/MTable.vue","../src/components/MTable.vue","../src/components/MTabs.vue","../src/components/MTabs.vue","../src/components/MTextField.vue","../src/components/MTextField.vue","../src/components/MTimeline.vue","../src/components/MTimeline.vue","../src/components/MTimePicker.vue","../src/components/MTimePicker.vue","../src/components/MTooltip.vue","../src/components/MTooltip.vue","../src/components/MTopAppBar.vue","../src/components/MTopAppBar.vue","../src/components/MTour.vue","../src/components/MTour.vue","../src/components/MTransferList.vue","../src/components/MTransferList.vue","../src/components/_MTreeNode.vue","../src/components/_MTreeNode.vue","../src/components/MTree.vue","../src/components/MTree.vue","../src/components/MTreeTable.vue","../src/components/MTreeTable.vue","../src/components/MVirtualTable.vue","../src/components/MVirtualTable.vue"],"sourcesContent":["import type { App } from 'vue'\nimport type { Palette } from './composables/useColorPalette'\n\nexport interface M3UIOptions {\n palette?: string\n customPalettes?: Palette[]\n}\n\nexport function createM3UI(options: M3UIOptions = {}) {\n return {\n install(_app: App) {\n if (options.palette && options.palette !== 'purple') {\n document.documentElement.setAttribute('data-palette', options.palette)\n localStorage.setItem('m3-palette', options.palette)\n }\n },\n }\n}\n","import { ref, watchEffect, onMounted, onUnmounted } from 'vue'\n\nexport type Theme = 'light' | 'dark' | 'system'\n\nconst theme = ref<Theme>((localStorage.getItem('m3-theme') as Theme) ?? 'system')\n\nlet _appliedDark: boolean | null = null\n\nfunction applyTheme(t: Theme) {\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches\n const isDark = t === 'dark' || (t === 'system' && prefersDark)\n\n const isChange = _appliedDark !== null && _appliedDark !== isDark\n\n if (isChange) {\n // Force reflow so transition property takes effect before the color change\n document.documentElement.classList.add('theme-transitioning')\n void document.documentElement.offsetHeight\n }\n\n document.documentElement.classList.toggle('dark', isDark)\n\n if (isChange) {\n setTimeout(() => document.documentElement.classList.remove('theme-transitioning'), 300)\n }\n\n _appliedDark = isDark\n}\n\n// Apply immediately on module load (before any component mounts) — no animation\napplyTheme(theme.value)\n\nexport function useTheme() {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n\n function onSystemChange() {\n if (theme.value === 'system') applyTheme('system')\n }\n\n watchEffect(() => {\n localStorage.setItem('m3-theme', theme.value)\n applyTheme(theme.value)\n })\n\n onMounted(() => mediaQuery.addEventListener('change', onSystemChange))\n onUnmounted(() => mediaQuery.removeEventListener('change', onSystemChange))\n\n function cycle() {\n const order: Theme[] = ['light', 'dark', 'system']\n const idx = order.indexOf(theme.value)\n theme.value = order[(idx + 1) % order.length]!\n }\n\n return { theme, cycle }\n}\n","import { ref, watchEffect } from 'vue'\n\nexport interface Palette {\n id: string\n label: string\n seed: string\n}\n\nexport const palettes: Palette[] = [\n { id: 'purple', label: 'Morado', seed: '#6750A4' },\n { id: 'indigo', label: 'Índigo', seed: '#4355B9' },\n { id: 'navy', label: 'Marino', seed: '#354BA0' },\n { id: 'blue', label: 'Azul', seed: '#005AC1' },\n { id: 'cyan', label: 'Cian', seed: '#006874' },\n { id: 'teal', label: 'Teal', seed: '#006B5F' },\n { id: 'green', label: 'Verde', seed: '#386A20' },\n { id: 'lime', label: 'Lima', seed: '#4C6706' },\n { id: 'olive', label: 'Oliva', seed: '#636118' },\n { id: 'amber', label: 'Ámbar', seed: '#785900' },\n { id: 'sand', label: 'Arena', seed: '#715C2E' },\n { id: 'orange', label: 'Naranja', seed: '#8B5000' },\n { id: 'deep-orange', label: 'Naranja oscuro', seed: '#96480A' },\n { id: 'brown', label: 'Marrón', seed: '#6E4C32' },\n { id: 'red', label: 'Rojo', seed: '#B82000' },\n { id: 'coral', label: 'Coral', seed: '#A03530' },\n { id: 'crimson', label: 'Carmesí', seed: '#9C4068' },\n { id: 'pink', label: 'Rosa', seed: '#9C4057' },\n { id: 'violet', label: 'Violeta', seed: '#7C39A4' },\n { id: 'slate', label: 'Pizarra', seed: '#4A6269' },\n]\n\nconst current = ref(localStorage.getItem('m3-palette') ?? 'purple')\n\nexport function useColorPalette() {\n watchEffect(() => {\n const id = current.value\n localStorage.setItem('m3-palette', id)\n\n if (id === 'purple') {\n document.documentElement.removeAttribute('data-palette')\n } else {\n document.documentElement.setAttribute('data-palette', id)\n }\n })\n\n function set(id: string) {\n document.documentElement.classList.add('theme-transitioning')\n void document.documentElement.offsetHeight\n current.value = id\n setTimeout(() => document.documentElement.classList.remove('theme-transitioning'), 300)\n }\n\n return { palette: current, palettes, set }\n}\n\n// Apply on module load so palette is visible before any component mounts\nconst saved = localStorage.getItem('m3-palette')\nif (saved && saved !== 'purple') {\n document.documentElement.setAttribute('data-palette', saved)\n}\n","import { ref } from 'vue'\n\nexport type ToastVariant = 'info' | 'success' | 'warning' | 'error'\nexport type ToastPosition =\n | 'top-left' | 'top-center' | 'top-right'\n | 'bottom-left' | 'bottom-center' | 'bottom-right'\n\nexport interface ToastAction { label: string; onClick: () => void }\n\nexport interface Toast {\n id: number\n message: string\n variant: ToastVariant\n duration: number\n action?: ToastAction\n}\n\nlet nextId = 1\n\nconst toasts = ref<Toast[]>([])\nconst position = ref<ToastPosition>('bottom-center')\n\nfunction dismiss(id: number) {\n toasts.value = toasts.value.filter((t) => t.id !== id)\n}\n\nfunction show(\n message: string,\n variant: ToastVariant = 'info',\n options: number | { duration?: number; action?: ToastAction } = {},\n) {\n const id = nextId++\n const opts = typeof options === 'number' ? { duration: options } : options\n const duration = opts.duration ?? (variant === 'error' ? 6000 : 4000)\n toasts.value.push({ id, message, variant, duration, action: opts.action })\n if (duration > 0) setTimeout(() => dismiss(id), duration)\n return id\n}\n\nconst success = (msg: string, opts?: { duration?: number; action?: ToastAction }) =>\n show(msg, 'success', opts ?? {})\nconst error = (msg: string, opts?: { duration?: number; action?: ToastAction }) =>\n show(msg, 'error', opts ?? {})\nconst warning = (msg: string, opts?: { duration?: number; action?: ToastAction }) =>\n show(msg, 'warning', opts ?? {})\nconst info = (msg: string, opts?: { duration?: number; action?: ToastAction }) =>\n show(msg, 'info', opts ?? {})\n\nexport function useToast() {\n return { toasts, position, show, success, error, warning, info, dismiss }\n}\n","import { ref, computed, onMounted, onBeforeUnmount, type Ref } from 'vue'\n\nconst M3_BG_CLASSES = [\n 'bg-surface-container-highest',\n 'bg-surface-container-high',\n 'bg-surface-container-low',\n 'bg-surface-container-lowest',\n 'bg-surface-container',\n 'bg-surface-variant',\n 'bg-surface-bright',\n 'bg-surface-dim',\n 'bg-surface',\n 'bg-background',\n 'bg-inverse-surface',\n 'bg-primary-container',\n 'bg-secondary-container',\n 'bg-tertiary-container',\n 'bg-error-container',\n 'bg-primary',\n 'bg-secondary',\n 'bg-tertiary',\n 'bg-error',\n] as const\n\nfunction findM3BgVar(el: HTMLElement): string | null {\n for (const cls of M3_BG_CLASSES) {\n if (el.classList.contains(cls)) return `var(--color-${cls.slice(3)})`\n }\n return null\n}\n\nfunction isTransparent(color: string): boolean {\n if (!color || color === 'transparent') return true\n const m = color.match(/^rgba?\\(([^)]+)\\)$/)\n if (m) {\n const parts = m[1]!.split(',').map((s) => s.trim())\n if (parts.length === 4 && parseFloat(parts[3]!) === 0) return true\n }\n return false\n}\n\n/**\n * Auto-detects the background behind `containerEl` and exposes it as `--field-bg`.\n * Prefers a CSS variable reference (e.g. var(--color-surface-container-low)) over a\n * raw computed color so the outlined label cutout transitions in sync with the rest\n * of the theme switch instead of lagging behind.\n *\n * @param containerEl The element that receives `--field-bg` as an inline style.\n * @param fieldBgProp Getter for the explicit `fieldBg` prop (overrides auto-detect).\n */\nexport function useFieldBg(\n containerEl: Ref<HTMLElement | null>,\n fieldBgProp: () => string | undefined,\n) {\n const detectedBg = ref<string>('var(--color-surface)')\n\n function applyFieldBg(value: string) {\n detectedBg.value = value\n containerEl.value?.style.setProperty('--field-bg', fieldBgProp() ?? value)\n }\n\n function resolveBg() {\n let el: HTMLElement | null = containerEl.value?.parentElement ?? null\n while (el) {\n const cssVar = findM3BgVar(el)\n if (cssVar) { applyFieldBg(cssVar); return }\n if (el === document.body) { applyFieldBg('var(--color-surface)'); return }\n const bg = getComputedStyle(el).backgroundColor\n if (!isTransparent(bg)) { applyFieldBg(bg); return }\n el = el.parentElement\n }\n applyFieldBg('var(--color-surface)')\n }\n\n let observer: MutationObserver | null = null\n\n onMounted(() => {\n resolveBg()\n observer = new MutationObserver(() => resolveBg())\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'style', 'data-theme'],\n })\n })\n\n onBeforeUnmount(() => observer?.disconnect())\n\n const resolvedFieldBg = computed(() => fieldBgProp() ?? detectedBg.value)\n\n return { resolvedFieldBg }\n}\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n type?: 'info' | 'success' | 'warning' | 'error'\n title?: string\n closeable?: boolean\n }>(),\n {\n type: 'info',\n closeable: false,\n },\n)\n\nconst emit = defineEmits<{ close: [] }>()\n\nconst config = {\n info: {\n icon: 'info',\n container: 'bg-primary-container text-on-primary-container',\n iconColor: 'text-primary',\n },\n success: {\n icon: 'check_circle',\n container: 'bg-success-container text-on-success-container',\n iconColor: 'text-success',\n },\n warning: {\n icon: 'warning',\n container: 'bg-tertiary-container text-on-tertiary-container',\n iconColor: 'text-tertiary',\n },\n error: {\n icon: 'error',\n container: 'bg-error-container text-on-error-container',\n iconColor: 'text-error',\n },\n}\n</script>\n\n<template>\n <div class=\"flex items-start gap-3 rounded-md p-4\" :class=\"config[type].container\">\n <MIcon\n :name=\"config[type].icon\"\n :size=\"20\"\n class=\"mt-0.5 shrink-0\"\n :class=\"config[type].iconColor\"\n />\n <div class=\"min-w-0 flex-1\">\n <p v-if=\"title\" class=\"mb-0.5 text-label-large font-medium\">{{ title }}</p>\n <div class=\"text-body-medium\">\n <slot />\n </div>\n <div v-if=\"$slots.actions\" class=\"mt-3 flex flex-wrap gap-2\">\n <slot name=\"actions\" />\n </div>\n </div>\n <button\n v-if=\"closeable\"\n type=\"button\"\n class=\"-mr-1 flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-on-surface/8\"\n aria-label=\"Cerrar\"\n @click=\"emit('close')\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n type?: 'info' | 'success' | 'warning' | 'error'\n title?: string\n closeable?: boolean\n }>(),\n {\n type: 'info',\n closeable: false,\n },\n)\n\nconst emit = defineEmits<{ close: [] }>()\n\nconst config = {\n info: {\n icon: 'info',\n container: 'bg-primary-container text-on-primary-container',\n iconColor: 'text-primary',\n },\n success: {\n icon: 'check_circle',\n container: 'bg-success-container text-on-success-container',\n iconColor: 'text-success',\n },\n warning: {\n icon: 'warning',\n container: 'bg-tertiary-container text-on-tertiary-container',\n iconColor: 'text-tertiary',\n },\n error: {\n icon: 'error',\n container: 'bg-error-container text-on-error-container',\n iconColor: 'text-error',\n },\n}\n</script>\n\n<template>\n <div class=\"flex items-start gap-3 rounded-md p-4\" :class=\"config[type].container\">\n <MIcon\n :name=\"config[type].icon\"\n :size=\"20\"\n class=\"mt-0.5 shrink-0\"\n :class=\"config[type].iconColor\"\n />\n <div class=\"min-w-0 flex-1\">\n <p v-if=\"title\" class=\"mb-0.5 text-label-large font-medium\">{{ title }}</p>\n <div class=\"text-body-medium\">\n <slot />\n </div>\n <div v-if=\"$slots.actions\" class=\"mt-3 flex flex-wrap gap-2\">\n <slot name=\"actions\" />\n </div>\n </div>\n <button\n v-if=\"closeable\"\n type=\"button\"\n class=\"-mr-1 flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-on-surface/8\"\n aria-label=\"Cerrar\"\n @click=\"emit('close')\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n color?: 'surface' | 'primary' | 'secondary' | 'tertiary'\n elevated?: boolean\n dense?: boolean\n}>(), { color: 'surface' })\n\nconst colorMap: Record<string, string> = {\n surface: 'bg-surface text-on-surface',\n primary: 'bg-primary text-on-primary',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n}\n</script>\n\n<template>\n <div\n class=\"flex w-full items-center gap-2 px-4 transition-shadow\"\n :class=\"[\n colorMap[color],\n elevated ? 'shadow-elevation-2' : '',\n dense ? 'h-12' : 'h-16',\n ]\"\n >\n <!-- Leading -->\n <div v-if=\"$slots.leading\" class=\"flex shrink-0 items-center\">\n <slot name=\"leading\" />\n </div>\n\n <!-- Content -->\n <div class=\"flex flex-1 items-center overflow-hidden\">\n <slot />\n </div>\n\n <!-- Trailing -->\n <div v-if=\"$slots.trailing\" class=\"flex shrink-0 items-center gap-1\">\n <slot name=\"trailing\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n color?: 'surface' | 'primary' | 'secondary' | 'tertiary'\n elevated?: boolean\n dense?: boolean\n}>(), { color: 'surface' })\n\nconst colorMap: Record<string, string> = {\n surface: 'bg-surface text-on-surface',\n primary: 'bg-primary text-on-primary',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n}\n</script>\n\n<template>\n <div\n class=\"flex w-full items-center gap-2 px-4 transition-shadow\"\n :class=\"[\n colorMap[color],\n elevated ? 'shadow-elevation-2' : '',\n dense ? 'h-12' : 'h-16',\n ]\"\n >\n <!-- Leading -->\n <div v-if=\"$slots.leading\" class=\"flex shrink-0 items-center\">\n <slot name=\"leading\" />\n </div>\n\n <!-- Content -->\n <div class=\"flex flex-1 items-center overflow-hidden\">\n <slot />\n </div>\n\n <!-- Trailing -->\n <div v-if=\"$slots.trailing\" class=\"flex shrink-0 items-center gap-1\">\n <slot name=\"trailing\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(defineProps<{ name: string; size?: number }>(), { size: 40 })\n\nconst initials = computed(() => {\n const parts = props.name.trim().split(/\\s+/).filter(Boolean)\n const first = parts[0]?.[0] ?? ''\n const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : ''\n return (first + last).toUpperCase() || '?'\n})\n</script>\n\n<template>\n <div\n class=\"inline-flex shrink-0 items-center justify-center rounded-full bg-primary-container font-medium text-on-primary-container\"\n :style=\"{ width: `${size}px`, height: `${size}px`, fontSize: `${Math.round(size * 0.4)}px` }\"\n >\n {{ initials }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(defineProps<{ name: string; size?: number }>(), { size: 40 })\n\nconst initials = computed(() => {\n const parts = props.name.trim().split(/\\s+/).filter(Boolean)\n const first = parts[0]?.[0] ?? ''\n const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : ''\n return (first + last).toUpperCase() || '?'\n})\n</script>\n\n<template>\n <div\n class=\"inline-flex shrink-0 items-center justify-center rounded-full bg-primary-container font-medium text-on-primary-container\"\n :style=\"{ width: `${size}px`, height: `${size}px`, fontSize: `${Math.round(size * 0.4)}px` }\"\n >\n {{ initials }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n count?: number\n dot?: boolean\n color?: 'primary' | 'error' | 'secondary' | 'tertiary'\n max?: number\n }>(),\n {\n color: 'error',\n max: 99,\n },\n)\n\nconst show = computed(() => props.dot || (props.count !== undefined && props.count > 0))\n\nconst label = computed(() => {\n if (props.dot || props.count === undefined) return ''\n return props.count > props.max ? `${props.max}+` : String(props.count)\n})\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary text-on-primary',\n error: 'bg-error text-on-error',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n}\n</script>\n\n<template>\n <span class=\"relative inline-flex\">\n <slot />\n <span\n v-if=\"show\"\n class=\"absolute -right-1 -top-1 flex items-center justify-center rounded-full font-medium leading-none\"\n :class=\"[\n colorMap[color],\n !label || dot ? 'h-2.5 w-2.5' : label.length > 2 ? 'h-5 min-w-[1.25rem] px-1 text-[10px]' : 'h-5 w-5 text-[10px]',\n ]\"\n >\n <span v-if=\"!dot\">{{ label }}</span>\n </span>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n count?: number\n dot?: boolean\n color?: 'primary' | 'error' | 'secondary' | 'tertiary'\n max?: number\n }>(),\n {\n color: 'error',\n max: 99,\n },\n)\n\nconst show = computed(() => props.dot || (props.count !== undefined && props.count > 0))\n\nconst label = computed(() => {\n if (props.dot || props.count === undefined) return ''\n return props.count > props.max ? `${props.max}+` : String(props.count)\n})\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary text-on-primary',\n error: 'bg-error text-on-error',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n}\n</script>\n\n<template>\n <span class=\"relative inline-flex\">\n <slot />\n <span\n v-if=\"show\"\n class=\"absolute -right-1 -top-1 flex items-center justify-center rounded-full font-medium leading-none\"\n :class=\"[\n colorMap[color],\n !label || dot ? 'h-2.5 w-2.5' : label.length > 2 ? 'h-5 min-w-[1.25rem] px-1 text-[10px]' : 'h-5 w-5 text-[10px]',\n ]\"\n >\n <span v-if=\"!dot\">{{ label }}</span>\n </span>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n modelValue: boolean\n title?: string\n fullHeight?: boolean\n}>(), { fullHeight: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\nconst close = () => emit('update:modelValue', false)\n\n// Drag-to-dismiss state\nconst dragY = ref(0)\nconst dragging = ref(false)\nlet startY = 0\n\nfunction onHandlePointerDown(e: PointerEvent) {\n dragging.value = true\n startY = e.clientY\n dragY.value = 0\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onHandlePointerMove(e: PointerEvent) {\n if (!dragging.value) return\n dragY.value = Math.max(0, e.clientY - startY)\n}\nfunction onHandlePointerUp() {\n if (dragY.value > 100) close()\n dragging.value = false\n dragY.value = 0\n}\n\nconst sheetStyle = computed(() => ({\n transform: `translateY(${dragY.value}px)`,\n transition: dragging.value ? 'none' : undefined,\n}))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"bs\" :duration=\"{ enter: 320, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[200] flex flex-col justify-end\">\n <!-- Scrim -->\n <div class=\"bs-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <div\n class=\"bs-panel relative flex w-full flex-col rounded-t-[28px] bg-surface-container-low shadow-elevation-3\"\n :class=\"fullHeight ? 'max-h-[92vh]' : 'max-h-[60vh]'\"\n :style=\"sheetStyle\"\n >\n <!-- Drag handle -->\n <div\n class=\"flex h-9 shrink-0 cursor-grab touch-none select-none items-center justify-center active:cursor-grabbing\"\n @pointerdown=\"onHandlePointerDown\"\n @pointermove=\"onHandlePointerMove\"\n @pointerup=\"onHandlePointerUp\"\n >\n <div class=\"h-1 w-8 rounded-full bg-on-surface-variant/40\" />\n </div>\n\n <!-- Header -->\n <div v-if=\"title\" class=\"flex shrink-0 items-center justify-between px-6 pb-4\">\n <h2 class=\"text-title-large text-on-surface\">{{ title }}</h2>\n <button\n type=\"button\"\n class=\"flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"20\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"flex-1 overflow-y-auto px-6 pb-6\">\n <slot />\n </div>\n\n <!-- Actions -->\n <div v-if=\"$slots.actions\" class=\"shrink-0 border-t border-outline-variant px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.bs-scrim {\n transition: opacity 300ms ease;\n}\n.bs-enter-from .bs-scrim,\n.bs-leave-to .bs-scrim {\n opacity: 0;\n}\n\n.bs-panel {\n transition:\n transform 320ms cubic-bezier(0.2, 0, 0, 1),\n opacity 240ms ease;\n}\n.bs-enter-from .bs-panel {\n transform: translateY(40%);\n opacity: 0;\n}\n.bs-leave-to .bs-panel {\n transform: translateY(100%) !important;\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n modelValue: boolean\n title?: string\n fullHeight?: boolean\n}>(), { fullHeight: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\nconst close = () => emit('update:modelValue', false)\n\n// Drag-to-dismiss state\nconst dragY = ref(0)\nconst dragging = ref(false)\nlet startY = 0\n\nfunction onHandlePointerDown(e: PointerEvent) {\n dragging.value = true\n startY = e.clientY\n dragY.value = 0\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onHandlePointerMove(e: PointerEvent) {\n if (!dragging.value) return\n dragY.value = Math.max(0, e.clientY - startY)\n}\nfunction onHandlePointerUp() {\n if (dragY.value > 100) close()\n dragging.value = false\n dragY.value = 0\n}\n\nconst sheetStyle = computed(() => ({\n transform: `translateY(${dragY.value}px)`,\n transition: dragging.value ? 'none' : undefined,\n}))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"bs\" :duration=\"{ enter: 320, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[200] flex flex-col justify-end\">\n <!-- Scrim -->\n <div class=\"bs-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <div\n class=\"bs-panel relative flex w-full flex-col rounded-t-[28px] bg-surface-container-low shadow-elevation-3\"\n :class=\"fullHeight ? 'max-h-[92vh]' : 'max-h-[60vh]'\"\n :style=\"sheetStyle\"\n >\n <!-- Drag handle -->\n <div\n class=\"flex h-9 shrink-0 cursor-grab touch-none select-none items-center justify-center active:cursor-grabbing\"\n @pointerdown=\"onHandlePointerDown\"\n @pointermove=\"onHandlePointerMove\"\n @pointerup=\"onHandlePointerUp\"\n >\n <div class=\"h-1 w-8 rounded-full bg-on-surface-variant/40\" />\n </div>\n\n <!-- Header -->\n <div v-if=\"title\" class=\"flex shrink-0 items-center justify-between px-6 pb-4\">\n <h2 class=\"text-title-large text-on-surface\">{{ title }}</h2>\n <button\n type=\"button\"\n class=\"flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"20\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"flex-1 overflow-y-auto px-6 pb-6\">\n <slot />\n </div>\n\n <!-- Actions -->\n <div v-if=\"$slots.actions\" class=\"shrink-0 border-t border-outline-variant px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.bs-scrim {\n transition: opacity 300ms ease;\n}\n.bs-enter-from .bs-scrim,\n.bs-leave-to .bs-scrim {\n opacity: 0;\n}\n\n.bs-panel {\n transition:\n transform 320ms cubic-bezier(0.2, 0, 0, 1),\n opacity 240ms ease;\n}\n.bs-enter-from .bs-panel {\n transform: translateY(40%);\n opacity: 0;\n}\n.bs-leave-to .bs-panel {\n transform: translateY(100%) !important;\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nexport interface BreadcrumbItem {\n label: string\n icon?: string\n to?: string\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n items: BreadcrumbItem[]\n separator?: string\n}>(), { separator: 'chevron_right' })\n\ndefineEmits<{ select: [BreadcrumbItem, number] }>()\n</script>\n\n<template>\n <nav aria-label=\"Breadcrumb\" class=\"flex items-center gap-1 overflow-x-auto text-label-large\">\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <!-- Separator -->\n <MIcon\n v-if=\"i > 0\"\n :name=\"separator\"\n :size=\"18\"\n class=\"shrink-0 text-on-surface-variant\"\n />\n\n <!-- Item -->\n <button\n v-if=\"i < items.length - 1 && !item.disabled\"\n type=\"button\"\n class=\"flex shrink-0 cursor-pointer items-center gap-1.5 rounded-sm px-1.5 py-1 text-primary transition-colors hover:bg-primary/8 focus-visible:outline-none\"\n @click=\"$emit('select', item, i)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n <span>{{ item.label }}</span>\n </button>\n\n <!-- Last item (current page) or disabled -->\n <span\n v-else\n class=\"flex shrink-0 items-center gap-1.5 px-1.5 py-1\"\n :class=\"item.disabled ? 'text-on-surface/38' : 'font-medium text-on-surface'\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n <span>{{ item.label }}</span>\n </span>\n </template>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nexport interface BreadcrumbItem {\n label: string\n icon?: string\n to?: string\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n items: BreadcrumbItem[]\n separator?: string\n}>(), { separator: 'chevron_right' })\n\ndefineEmits<{ select: [BreadcrumbItem, number] }>()\n</script>\n\n<template>\n <nav aria-label=\"Breadcrumb\" class=\"flex items-center gap-1 overflow-x-auto text-label-large\">\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <!-- Separator -->\n <MIcon\n v-if=\"i > 0\"\n :name=\"separator\"\n :size=\"18\"\n class=\"shrink-0 text-on-surface-variant\"\n />\n\n <!-- Item -->\n <button\n v-if=\"i < items.length - 1 && !item.disabled\"\n type=\"button\"\n class=\"flex shrink-0 cursor-pointer items-center gap-1.5 rounded-sm px-1.5 py-1 text-primary transition-colors hover:bg-primary/8 focus-visible:outline-none\"\n @click=\"$emit('select', item, i)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n <span>{{ item.label }}</span>\n </button>\n\n <!-- Last item (current page) or disabled -->\n <span\n v-else\n class=\"flex shrink-0 items-center gap-1.5 px-1.5 py-1\"\n :class=\"item.disabled ? 'text-on-surface/38' : 'font-medium text-on-surface'\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n <span>{{ item.label }}</span>\n </span>\n </template>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n size?: number;\n wavy?: boolean;\n }>(),\n { size: 20, wavy: false },\n);\n\nconst STROKE = 3;\nconst BUMPS = 9;\n\n// amp fraction of r = 0.25 → max radius = r * 1.25\n// Constrain so that max_r + STROKE/2 ≤ size/2 - 1 (1px margin from edge)\nconst r = computed(() => (props.size / 2 - 1 - STROKE / 2) / 1.25);\nconst cx = computed(() => props.size / 2);\n\n// Build the full bumpy-circle path and its total length.\nconst wavyData = computed(() => {\n const CX = cx.value;\n const R = r.value;\n const amp = R * 0.08;\n const segs = BUMPS * 24; // smooth curve\n\n const pts: string[] = [];\n let len = 0;\n let px = 0,\n py = 0;\n\n for (let i = 0; i <= segs; i++) {\n const theta = (2 * Math.PI * i) / segs - Math.PI / 2;\n const rr = R + amp * Math.sin(BUMPS * theta);\n const x = CX + rr * Math.cos(theta);\n const y = CX + rr * Math.sin(theta);\n if (i > 0) len += Math.sqrt((x - px) ** 2 + (y - py) ** 2);\n pts.push(`${i === 0 ? \"M\" : \"L\"}${x.toFixed(2)},${y.toFixed(2)}`);\n px = x;\n py = y;\n }\n\n // Visible arc ~58% of the circumference, gap fills the rest.\n const visible = len * 0.58;\n const gap = len - visible;\n const dash = `${visible.toFixed(1)} ${gap.toFixed(1)}`;\n\n // The wave \"travels\" by shifting dashoffset over exactly one full length,\n // so the crests slide around the path independently of the rotation.\n return { path: pts.join(\"\") + \"Z\", dash, len: len.toFixed(1) };\n});\n</script>\n\n<template>\n <span\n class=\"inline-flex shrink-0 items-center justify-center\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n role=\"status\"\n aria-label=\"Cargando\"\n >\n <!-- Standard circular spinner -->\n <span\n v-if=\"!wavy\"\n class=\"block h-full w-full animate-spin rounded-full border-2 border-current border-t-transparent\"\n />\n\n <!-- Wavy spinner (M3 Expressive): the whole shape rotates AND the wave\n travels along the stroke via dashoffset, giving the snake-like flow. -->\n <svg\n v-else\n :width=\"size\"\n :height=\"size\"\n :viewBox=\"`0 0 ${size} ${size}`\"\n fill=\"none\"\n class=\"animate-[m3-wavy-spin_2.8s_linear_infinite]\"\n :style=\"`transform-origin: ${cx}px ${cx}px`\"\n >\n <path\n :d=\"wavyData.path\"\n stroke=\"currentColor\"\n :stroke-width=\"STROKE\"\n stroke-linecap=\"round\"\n :stroke-dasharray=\"wavyData.dash\"\n class=\"animate-[m3-wavy-travel_2s_linear_infinite]\"\n :style=\"{ '--m3-wave-len': wavyData.len }\"\n />\n </svg>\n </span>\n</template>\n\n<style>\n/* The SVG element rotates the whole bumpy circle. */\n@keyframes m3-wavy-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* The stroke's dashoffset slides by one full path length, so the crests\n appear to crawl along the circle — the \"snake\" motion of M3 Expressive.\n Negative direction makes the wave travel forward relative to the spin. */\n@keyframes m3-wavy-travel {\n from {\n stroke-dashoffset: 0;\n }\n to {\n stroke-dashoffset: calc(var(--m3-wave-len) * -1px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .animate-\\[m3-wavy-spin_2\\.8s_linear_infinite\\] {\n animation: m3-wavy-spin 2.8s linear infinite;\n }\n .animate-\\[m3-wavy-travel_2s_linear_infinite\\] {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n size?: number;\n wavy?: boolean;\n }>(),\n { size: 20, wavy: false },\n);\n\nconst STROKE = 3;\nconst BUMPS = 9;\n\n// amp fraction of r = 0.25 → max radius = r * 1.25\n// Constrain so that max_r + STROKE/2 ≤ size/2 - 1 (1px margin from edge)\nconst r = computed(() => (props.size / 2 - 1 - STROKE / 2) / 1.25);\nconst cx = computed(() => props.size / 2);\n\n// Build the full bumpy-circle path and its total length.\nconst wavyData = computed(() => {\n const CX = cx.value;\n const R = r.value;\n const amp = R * 0.08;\n const segs = BUMPS * 24; // smooth curve\n\n const pts: string[] = [];\n let len = 0;\n let px = 0,\n py = 0;\n\n for (let i = 0; i <= segs; i++) {\n const theta = (2 * Math.PI * i) / segs - Math.PI / 2;\n const rr = R + amp * Math.sin(BUMPS * theta);\n const x = CX + rr * Math.cos(theta);\n const y = CX + rr * Math.sin(theta);\n if (i > 0) len += Math.sqrt((x - px) ** 2 + (y - py) ** 2);\n pts.push(`${i === 0 ? \"M\" : \"L\"}${x.toFixed(2)},${y.toFixed(2)}`);\n px = x;\n py = y;\n }\n\n // Visible arc ~58% of the circumference, gap fills the rest.\n const visible = len * 0.58;\n const gap = len - visible;\n const dash = `${visible.toFixed(1)} ${gap.toFixed(1)}`;\n\n // The wave \"travels\" by shifting dashoffset over exactly one full length,\n // so the crests slide around the path independently of the rotation.\n return { path: pts.join(\"\") + \"Z\", dash, len: len.toFixed(1) };\n});\n</script>\n\n<template>\n <span\n class=\"inline-flex shrink-0 items-center justify-center\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n role=\"status\"\n aria-label=\"Cargando\"\n >\n <!-- Standard circular spinner -->\n <span\n v-if=\"!wavy\"\n class=\"block h-full w-full animate-spin rounded-full border-2 border-current border-t-transparent\"\n />\n\n <!-- Wavy spinner (M3 Expressive): the whole shape rotates AND the wave\n travels along the stroke via dashoffset, giving the snake-like flow. -->\n <svg\n v-else\n :width=\"size\"\n :height=\"size\"\n :viewBox=\"`0 0 ${size} ${size}`\"\n fill=\"none\"\n class=\"animate-[m3-wavy-spin_2.8s_linear_infinite]\"\n :style=\"`transform-origin: ${cx}px ${cx}px`\"\n >\n <path\n :d=\"wavyData.path\"\n stroke=\"currentColor\"\n :stroke-width=\"STROKE\"\n stroke-linecap=\"round\"\n :stroke-dasharray=\"wavyData.dash\"\n class=\"animate-[m3-wavy-travel_2s_linear_infinite]\"\n :style=\"{ '--m3-wave-len': wavyData.len }\"\n />\n </svg>\n </span>\n</template>\n\n<style>\n/* The SVG element rotates the whole bumpy circle. */\n@keyframes m3-wavy-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* The stroke's dashoffset slides by one full path length, so the crests\n appear to crawl along the circle — the \"snake\" motion of M3 Expressive.\n Negative direction makes the wave travel forward relative to the spin. */\n@keyframes m3-wavy-travel {\n from {\n stroke-dashoffset: 0;\n }\n to {\n stroke-dashoffset: calc(var(--m3-wave-len) * -1px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .animate-\\[m3-wavy-spin_2\\.8s_linear_infinite\\] {\n animation: m3-wavy-spin 2.8s linear infinite;\n }\n .animate-\\[m3-wavy-travel_2s_linear_infinite\\] {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MSpinner from './MSpinner.vue'\nimport MIcon from './MIcon.vue'\n\nconst NAMED_COLORS = ['primary', 'error'] as const\ntype NamedColor = (typeof NAMED_COLORS)[number]\n\nconst props = withDefaults(\n defineProps<{\n variant?: 'filled' | 'tonal' | 'outlined' | 'text' | 'elevated'\n /**\n * Named semantic color ('primary' | 'error') OR any CSS color string\n * ('red', '#e91e63', 'oklch(0.6 0.2 0)', …).\n * When a CSS color is passed, --color-primary is overridden for this button.\n */\n color?: string\n type?: 'button' | 'submit' | 'reset'\n disabled?: boolean\n loading?: boolean\n icon?: string\n }>(),\n {\n variant: 'filled',\n color: 'primary',\n type: 'button',\n disabled: false,\n loading: false,\n },\n)\n\nconst isCustomColor = computed(\n () => !!props.color && !(NAMED_COLORS as readonly string[]).includes(props.color),\n)\n\nconst customStyle = computed(() => {\n if (!isCustomColor.value) return undefined\n return {\n '--color-primary': props.color,\n '--color-on-primary': '#ffffff',\n '--color-primary-container': props.color + '33',\n '--color-on-primary-container': props.color,\n }\n})\n\nconst isError = computed(() => props.color === 'error')\n\n// State-layer overlay: before: pseudo-element uses currentColor (the button's text color)\n// so it's always the correct M3 state-layer color for every variant automatically.\nconst base =\n 'relative inline-flex items-center justify-center gap-2 h-10 rounded-full text-label-large font-medium ' +\n 'whitespace-nowrap overflow-hidden transition-[box-shadow,background-color,color] duration-150 select-none cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38] disabled:shadow-none ' +\n \"before:content-[''] before:pointer-events-none before:absolute before:inset-0 \" +\n 'before:bg-current before:opacity-0 before:transition-opacity before:duration-150 ' +\n 'enabled:hover:before:opacity-[0.08] enabled:active:before:opacity-[0.12]'\n\nconst variantClasses = computed(() => {\n const err = isError.value\n switch (props.variant) {\n case 'filled':\n return err\n ? 'px-6 bg-error text-on-error enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'px-6 bg-primary text-on-primary enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'tonal':\n return err\n ? 'px-6 bg-error-container text-on-error-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'px-6 bg-secondary-container text-on-secondary-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'elevated':\n return err\n ? 'px-6 bg-surface-container-low text-error shadow-elevation-1 enabled:hover:shadow-elevation-2'\n : 'px-6 bg-surface-container-low text-primary shadow-elevation-1 enabled:hover:shadow-elevation-2'\n case 'outlined':\n return err\n ? 'px-6 border border-error text-error'\n : 'px-6 border border-outline text-primary'\n case 'text':\n return err\n ? 'px-3 text-error'\n : 'px-3 text-primary'\n default:\n return ''\n }\n})\n\nfunction createRipple(event: PointerEvent) {\n if (props.disabled || props.loading) return\n const button = event.currentTarget as HTMLElement\n const rect = button.getBoundingClientRect()\n const d = Math.max(rect.width, rect.height) * 2\n const el = document.createElement('span')\n el.className = 'm3-ripple'\n el.style.cssText = `width:${d}px;height:${d}px;top:${event.clientY - rect.top - d / 2}px;left:${event.clientX - rect.left - d / 2}px`\n button.appendChild(el)\n el.addEventListener('animationend', () => el.remove(), { once: true })\n}\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :class=\"[base, variantClasses]\"\n :style=\"customStyle\"\n @pointerdown=\"createRipple\"\n >\n <MSpinner v-if=\"loading\" :size=\"18\" />\n <MIcon v-else-if=\"icon\" :name=\"icon\" :size=\"20\" />\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MSpinner from './MSpinner.vue'\nimport MIcon from './MIcon.vue'\n\nconst NAMED_COLORS = ['primary', 'error'] as const\ntype NamedColor = (typeof NAMED_COLORS)[number]\n\nconst props = withDefaults(\n defineProps<{\n variant?: 'filled' | 'tonal' | 'outlined' | 'text' | 'elevated'\n /**\n * Named semantic color ('primary' | 'error') OR any CSS color string\n * ('red', '#e91e63', 'oklch(0.6 0.2 0)', …).\n * When a CSS color is passed, --color-primary is overridden for this button.\n */\n color?: string\n type?: 'button' | 'submit' | 'reset'\n disabled?: boolean\n loading?: boolean\n icon?: string\n }>(),\n {\n variant: 'filled',\n color: 'primary',\n type: 'button',\n disabled: false,\n loading: false,\n },\n)\n\nconst isCustomColor = computed(\n () => !!props.color && !(NAMED_COLORS as readonly string[]).includes(props.color),\n)\n\nconst customStyle = computed(() => {\n if (!isCustomColor.value) return undefined\n return {\n '--color-primary': props.color,\n '--color-on-primary': '#ffffff',\n '--color-primary-container': props.color + '33',\n '--color-on-primary-container': props.color,\n }\n})\n\nconst isError = computed(() => props.color === 'error')\n\n// State-layer overlay: before: pseudo-element uses currentColor (the button's text color)\n// so it's always the correct M3 state-layer color for every variant automatically.\nconst base =\n 'relative inline-flex items-center justify-center gap-2 h-10 rounded-full text-label-large font-medium ' +\n 'whitespace-nowrap overflow-hidden transition-[box-shadow,background-color,color] duration-150 select-none cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38] disabled:shadow-none ' +\n \"before:content-[''] before:pointer-events-none before:absolute before:inset-0 \" +\n 'before:bg-current before:opacity-0 before:transition-opacity before:duration-150 ' +\n 'enabled:hover:before:opacity-[0.08] enabled:active:before:opacity-[0.12]'\n\nconst variantClasses = computed(() => {\n const err = isError.value\n switch (props.variant) {\n case 'filled':\n return err\n ? 'px-6 bg-error text-on-error enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'px-6 bg-primary text-on-primary enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'tonal':\n return err\n ? 'px-6 bg-error-container text-on-error-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'px-6 bg-secondary-container text-on-secondary-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'elevated':\n return err\n ? 'px-6 bg-surface-container-low text-error shadow-elevation-1 enabled:hover:shadow-elevation-2'\n : 'px-6 bg-surface-container-low text-primary shadow-elevation-1 enabled:hover:shadow-elevation-2'\n case 'outlined':\n return err\n ? 'px-6 border border-error text-error'\n : 'px-6 border border-outline text-primary'\n case 'text':\n return err\n ? 'px-3 text-error'\n : 'px-3 text-primary'\n default:\n return ''\n }\n})\n\nfunction createRipple(event: PointerEvent) {\n if (props.disabled || props.loading) return\n const button = event.currentTarget as HTMLElement\n const rect = button.getBoundingClientRect()\n const d = Math.max(rect.width, rect.height) * 2\n const el = document.createElement('span')\n el.className = 'm3-ripple'\n el.style.cssText = `width:${d}px;height:${d}px;top:${event.clientY - rect.top - d / 2}px;left:${event.clientX - rect.left - d / 2}px`\n button.appendChild(el)\n el.addEventListener('animationend', () => el.remove(), { once: true })\n}\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :class=\"[base, variantClasses]\"\n :style=\"customStyle\"\n @pointerdown=\"createRipple\"\n >\n <MSpinner v-if=\"loading\" :size=\"18\" />\n <MIcon v-else-if=\"icon\" :name=\"icon\" :size=\"20\" />\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n icon: string\n label: string\n variant?: 'standard' | 'filled' | 'tonal' | 'outlined'\n disabled?: boolean\n size?: number\n }>(),\n {\n variant: 'standard',\n disabled: false,\n size: 40,\n },\n)\n\nconst base =\n 'inline-flex shrink-0 items-center justify-center rounded-full transition-colors duration-150 cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38]'\n\nconst variantClasses = computed(() => {\n switch (props.variant) {\n case 'filled':\n return 'bg-primary text-on-primary hover:shadow-elevation-1'\n case 'tonal':\n return 'bg-secondary-container text-on-secondary-container hover:shadow-elevation-1'\n case 'outlined':\n return 'border border-outline text-on-surface-variant hover:bg-on-surface/8'\n default:\n return 'text-on-surface-variant hover:bg-on-surface/8 active:bg-on-surface/12'\n }\n})\n</script>\n\n<template>\n <button\n type=\"button\"\n :aria-label=\"label\"\n :title=\"label\"\n :disabled=\"disabled\"\n :class=\"[base, variantClasses]\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n >\n <MIcon :name=\"icon\" :size=\"Math.round(size * 0.55)\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n icon: string\n label: string\n variant?: 'standard' | 'filled' | 'tonal' | 'outlined'\n disabled?: boolean\n size?: number\n }>(),\n {\n variant: 'standard',\n disabled: false,\n size: 40,\n },\n)\n\nconst base =\n 'inline-flex shrink-0 items-center justify-center rounded-full transition-colors duration-150 cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38]'\n\nconst variantClasses = computed(() => {\n switch (props.variant) {\n case 'filled':\n return 'bg-primary text-on-primary hover:shadow-elevation-1'\n case 'tonal':\n return 'bg-secondary-container text-on-secondary-container hover:shadow-elevation-1'\n case 'outlined':\n return 'border border-outline text-on-surface-variant hover:bg-on-surface/8'\n default:\n return 'text-on-surface-variant hover:bg-on-surface/8 active:bg-on-surface/12'\n }\n})\n</script>\n\n<template>\n <button\n type=\"button\"\n :aria-label=\"label\"\n :title=\"label\"\n :disabled=\"disabled\"\n :class=\"[base, variantClasses]\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n >\n <MIcon :name=\"icon\" :size=\"Math.round(size * 0.55)\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface CalendarEvent {\n id: string | number\n title: string\n date: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n icon?: string\n}\n\nconst props = withDefaults(defineProps<{\n events?: CalendarEvent[]\n locale?: string\n}>(), { events: () => [], locale: 'es-ES' })\n\nconst emit = defineEmits<{\n dateClick: [string]\n eventClick: [CalendarEvent]\n}>()\n\nconst viewDate = ref(new Date())\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'short' })\n return Array.from({ length: 7 }, (_, i) => f.format(new Date(2024, 0, i + 1)))\n})()\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(viewDate.value)\n)\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`\n}\n\nconst todayIso = fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\ninterface CalendarDay {\n date: number\n iso: string\n current: boolean\n events: CalendarEvent[]\n}\n\nconst calendarDays = computed<CalendarDay[]>(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: CalendarDay[] = []\n\n const eventMap = new Map<string, CalendarEvent[]>()\n for (const ev of props.events) {\n if (!eventMap.has(ev.date)) eventMap.set(ev.date, [])\n eventMap.get(ev.date)!.push(ev)\n }\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, events: eventMap.get(iso) ?? [] })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, events: eventMap.get(iso) ?? [] })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, events: eventMap.get(iso) ?? [] })\n }\n return days\n})\n\nfunction prevMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() - 1); viewDate.value = d }\nfunction nextMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() + 1); viewDate.value = d }\nfunction goToday() { viewDate.value = new Date() }\n\nconst eventColor: Record<string, string> = {\n primary: 'bg-primary text-on-primary',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n error: 'bg-error text-on-error',\n success: 'bg-success text-on-success',\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-3\">\n <div class=\"flex items-center gap-1\">\n <MIconButton icon=\"chevron_left\" label=\"Mes anterior\" :size=\"36\" @click=\"prevMonth\" />\n <MIconButton icon=\"chevron_right\" label=\"Mes siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n <h3 class=\"text-title-medium font-medium capitalize text-on-surface\">{{ monthLabel }}</h3>\n <button\n type=\"button\"\n class=\"cursor-pointer rounded-full border border-outline px-3 py-1 text-label-medium text-on-surface transition-colors hover:bg-on-surface/8\"\n @click=\"goToday\"\n >\n Hoy\n </button>\n </div>\n\n <!-- Weekday headers -->\n <div class=\"grid grid-cols-7 border-b border-outline-variant bg-surface-container-high\">\n <div\n v-for=\"wd in WEEKDAYS\"\n :key=\"wd\"\n class=\"py-2 text-center text-label-small font-medium uppercase text-on-surface-variant\"\n >\n {{ wd }}\n </div>\n </div>\n\n <!-- Days grid -->\n <div class=\"grid grid-cols-7\">\n <div\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n class=\"flex min-h-[80px] cursor-pointer flex-col border-b border-r border-outline-variant/50 p-1.5 transition-colors hover:bg-on-surface/[0.03]\"\n :class=\"[\n !day.current ? 'bg-surface-container-lowest/50' : 'bg-surface',\n (i + 1) % 7 === 0 ? 'border-r-0' : '',\n i >= 35 ? 'border-b-0' : '',\n ]\"\n @click=\"emit('dateClick', day.iso)\"\n >\n <!-- Day number -->\n <span\n class=\"mb-0.5 flex h-6 w-6 items-center justify-center self-end rounded-full text-label-medium\"\n :class=\"\n day.iso === todayIso\n ? 'bg-primary text-on-primary font-medium'\n : day.current\n ? 'text-on-surface'\n : 'text-on-surface-variant/40'\n \"\n >\n {{ day.date }}\n </span>\n\n <!-- Events -->\n <div v-if=\"day.events.length\" class=\"flex flex-col gap-0.5\">\n <button\n v-for=\"ev in day.events.slice(0, 2)\"\n :key=\"ev.id\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-1 truncate rounded px-1 py-0.5 text-left text-label-small transition-opacity hover:opacity-80\"\n :class=\"eventColor[ev.color ?? 'primary']\"\n @click.stop=\"emit('eventClick', ev)\"\n >\n <MIcon v-if=\"ev.icon\" :name=\"ev.icon\" :size=\"12\" />\n <span class=\"truncate\">{{ ev.title }}</span>\n </button>\n <span\n v-if=\"day.events.length > 2\"\n class=\"px-1 text-label-small text-on-surface-variant\"\n >\n +{{ day.events.length - 2 }} más\n </span>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface CalendarEvent {\n id: string | number\n title: string\n date: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n icon?: string\n}\n\nconst props = withDefaults(defineProps<{\n events?: CalendarEvent[]\n locale?: string\n}>(), { events: () => [], locale: 'es-ES' })\n\nconst emit = defineEmits<{\n dateClick: [string]\n eventClick: [CalendarEvent]\n}>()\n\nconst viewDate = ref(new Date())\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'short' })\n return Array.from({ length: 7 }, (_, i) => f.format(new Date(2024, 0, i + 1)))\n})()\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(viewDate.value)\n)\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`\n}\n\nconst todayIso = fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\ninterface CalendarDay {\n date: number\n iso: string\n current: boolean\n events: CalendarEvent[]\n}\n\nconst calendarDays = computed<CalendarDay[]>(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: CalendarDay[] = []\n\n const eventMap = new Map<string, CalendarEvent[]>()\n for (const ev of props.events) {\n if (!eventMap.has(ev.date)) eventMap.set(ev.date, [])\n eventMap.get(ev.date)!.push(ev)\n }\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, events: eventMap.get(iso) ?? [] })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, events: eventMap.get(iso) ?? [] })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, events: eventMap.get(iso) ?? [] })\n }\n return days\n})\n\nfunction prevMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() - 1); viewDate.value = d }\nfunction nextMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() + 1); viewDate.value = d }\nfunction goToday() { viewDate.value = new Date() }\n\nconst eventColor: Record<string, string> = {\n primary: 'bg-primary text-on-primary',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n error: 'bg-error text-on-error',\n success: 'bg-success text-on-success',\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-3\">\n <div class=\"flex items-center gap-1\">\n <MIconButton icon=\"chevron_left\" label=\"Mes anterior\" :size=\"36\" @click=\"prevMonth\" />\n <MIconButton icon=\"chevron_right\" label=\"Mes siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n <h3 class=\"text-title-medium font-medium capitalize text-on-surface\">{{ monthLabel }}</h3>\n <button\n type=\"button\"\n class=\"cursor-pointer rounded-full border border-outline px-3 py-1 text-label-medium text-on-surface transition-colors hover:bg-on-surface/8\"\n @click=\"goToday\"\n >\n Hoy\n </button>\n </div>\n\n <!-- Weekday headers -->\n <div class=\"grid grid-cols-7 border-b border-outline-variant bg-surface-container-high\">\n <div\n v-for=\"wd in WEEKDAYS\"\n :key=\"wd\"\n class=\"py-2 text-center text-label-small font-medium uppercase text-on-surface-variant\"\n >\n {{ wd }}\n </div>\n </div>\n\n <!-- Days grid -->\n <div class=\"grid grid-cols-7\">\n <div\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n class=\"flex min-h-[80px] cursor-pointer flex-col border-b border-r border-outline-variant/50 p-1.5 transition-colors hover:bg-on-surface/[0.03]\"\n :class=\"[\n !day.current ? 'bg-surface-container-lowest/50' : 'bg-surface',\n (i + 1) % 7 === 0 ? 'border-r-0' : '',\n i >= 35 ? 'border-b-0' : '',\n ]\"\n @click=\"emit('dateClick', day.iso)\"\n >\n <!-- Day number -->\n <span\n class=\"mb-0.5 flex h-6 w-6 items-center justify-center self-end rounded-full text-label-medium\"\n :class=\"\n day.iso === todayIso\n ? 'bg-primary text-on-primary font-medium'\n : day.current\n ? 'text-on-surface'\n : 'text-on-surface-variant/40'\n \"\n >\n {{ day.date }}\n </span>\n\n <!-- Events -->\n <div v-if=\"day.events.length\" class=\"flex flex-col gap-0.5\">\n <button\n v-for=\"ev in day.events.slice(0, 2)\"\n :key=\"ev.id\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-1 truncate rounded px-1 py-0.5 text-left text-label-small transition-opacity hover:opacity-80\"\n :class=\"eventColor[ev.color ?? 'primary']\"\n @click.stop=\"emit('eventClick', ev)\"\n >\n <MIcon v-if=\"ev.icon\" :name=\"ev.icon\" :size=\"12\" />\n <span class=\"truncate\">{{ ev.title }}</span>\n </button>\n <span\n v-if=\"day.events.length > 2\"\n class=\"px-1 text-label-small text-on-surface-variant\"\n >\n +{{ day.events.length - 2 }} más\n </span>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n variant?: 'elevated' | 'filled' | 'outlined'\n clickable?: boolean\n elevated?: boolean\n /** src URL for a full-bleed header image */\n image?: string\n imageAlt?: string\n imageHeight?: string\n }>(),\n { variant: 'elevated', clickable: false, elevated: false },\n)\n\nconst resolvedVariant = computed(() => (props.elevated ? 'elevated' : props.variant))\n\nconst variantClasses: Record<string, string> = {\n elevated: 'bg-surface-container-low shadow-elevation-1',\n filled: 'bg-surface-container-highest',\n outlined: 'bg-surface border border-outline-variant',\n}\n\n// Expose the card's background as --field-bg so outlined text-field labels\n// inside the card automatically match without needing the fieldBg prop.\nconst fieldBgByVariant: Record<string, string> = {\n elevated: 'var(--color-surface-container-low)',\n filled: 'var(--color-surface-container-highest)',\n outlined: 'var(--color-surface)',\n}\n</script>\n\n<template>\n <div\n class=\"overflow-hidden rounded-md transition-shadow duration-150\"\n :class=\"[\n variantClasses[resolvedVariant],\n clickable ? 'cursor-pointer hover:shadow-elevation-2 active:shadow-elevation-1' : '',\n ]\"\n :style=\"{ '--field-bg': fieldBgByVariant[resolvedVariant] }\"\n >\n <!-- Optional header image -->\n <div v-if=\"image || $slots.media\" :class=\"['w-full overflow-hidden', imageHeight ?? 'h-48']\">\n <img\n v-if=\"image\"\n :src=\"image\"\n :alt=\"imageAlt ?? ''\"\n class=\"h-full w-full object-cover\"\n />\n <slot v-else name=\"media\" />\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n variant?: 'elevated' | 'filled' | 'outlined'\n clickable?: boolean\n elevated?: boolean\n /** src URL for a full-bleed header image */\n image?: string\n imageAlt?: string\n imageHeight?: string\n }>(),\n { variant: 'elevated', clickable: false, elevated: false },\n)\n\nconst resolvedVariant = computed(() => (props.elevated ? 'elevated' : props.variant))\n\nconst variantClasses: Record<string, string> = {\n elevated: 'bg-surface-container-low shadow-elevation-1',\n filled: 'bg-surface-container-highest',\n outlined: 'bg-surface border border-outline-variant',\n}\n\n// Expose the card's background as --field-bg so outlined text-field labels\n// inside the card automatically match without needing the fieldBg prop.\nconst fieldBgByVariant: Record<string, string> = {\n elevated: 'var(--color-surface-container-low)',\n filled: 'var(--color-surface-container-highest)',\n outlined: 'var(--color-surface)',\n}\n</script>\n\n<template>\n <div\n class=\"overflow-hidden rounded-md transition-shadow duration-150\"\n :class=\"[\n variantClasses[resolvedVariant],\n clickable ? 'cursor-pointer hover:shadow-elevation-2 active:shadow-elevation-1' : '',\n ]\"\n :style=\"{ '--field-bg': fieldBgByVariant[resolvedVariant] }\"\n >\n <!-- Optional header image -->\n <div v-if=\"image || $slots.media\" :class=\"['w-full overflow-hidden', imageHeight ?? 'h-48']\">\n <img\n v-if=\"image\"\n :src=\"image\"\n :alt=\"imageAlt ?? ''\"\n class=\"h-full w-full object-cover\"\n />\n <slot v-else name=\"media\" />\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from \"./MIcon.vue\";\n\nwithDefaults(\n defineProps<{\n modelValue: boolean;\n indeterminate?: boolean;\n disabled?: boolean;\n label?: string;\n }>(),\n { indeterminate: false, disabled: false },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [boolean] }>();\n</script>\n\n<template>\n <label\n class=\"inline-flex items-center gap-2 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span\n class=\"relative inline-flex h-4.5 w-4.5 shrink-0 items-center justify-center rounded-[3px] border-2 transition-colors\"\n :class=\"\n modelValue || indeterminate\n ? 'border-primary bg-primary text-on-primary'\n : 'border-on-surface-variant text-transparent'\n \"\n >\n <input\n type=\"checkbox\"\n class=\"sr-only\"\n :checked=\"modelValue\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', !modelValue)\"\n />\n <MIcon\n :name=\"indeterminate ? 'remove' : 'check'\"\n :size=\"14\"\n class=\"transition-[opacity,transform] duration-150\"\n :class=\"modelValue || indeterminate ? 'scale-100 opacity-100' : 'scale-0 opacity-0'\"\n />\n </span>\n <span v-if=\"label || $slots.default\" class=\"text-body-large text-on-surface\">\n <slot>{{ label }}</slot>\n </span>\n </label>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from \"./MIcon.vue\";\n\nwithDefaults(\n defineProps<{\n modelValue: boolean;\n indeterminate?: boolean;\n disabled?: boolean;\n label?: string;\n }>(),\n { indeterminate: false, disabled: false },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [boolean] }>();\n</script>\n\n<template>\n <label\n class=\"inline-flex items-center gap-2 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span\n class=\"relative inline-flex h-4.5 w-4.5 shrink-0 items-center justify-center rounded-[3px] border-2 transition-colors\"\n :class=\"\n modelValue || indeterminate\n ? 'border-primary bg-primary text-on-primary'\n : 'border-on-surface-variant text-transparent'\n \"\n >\n <input\n type=\"checkbox\"\n class=\"sr-only\"\n :checked=\"modelValue\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', !modelValue)\"\n />\n <MIcon\n :name=\"indeterminate ? 'remove' : 'check'\"\n :size=\"14\"\n class=\"transition-[opacity,transform] duration-150\"\n :class=\"modelValue || indeterminate ? 'scale-100 opacity-100' : 'scale-0 opacity-0'\"\n />\n </span>\n <span v-if=\"label || $slots.default\" class=\"text-body-large text-on-surface\">\n <slot>{{ label }}</slot>\n </span>\n </label>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst NAMED_TONES = ['neutral', 'primary', 'success', 'error', 'tertiary', 'secondary'] as const\ntype NamedTone = (typeof NAMED_TONES)[number]\n\nconst props = withDefaults(\n defineProps<{\n tone?: string // named tone OR CSS color string\n selected?: boolean\n removable?: boolean\n clickable?: boolean\n disabled?: boolean\n icon?: string\n }>(),\n {\n tone: 'neutral',\n selected: false,\n removable: false,\n clickable: false,\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{ click: []; remove: [] }>()\n\nconst isCustomColor = computed(\n () => !!props.tone && !(NAMED_TONES as readonly string[]).includes(props.tone),\n)\n\n// When a CSS color is passed, apply it via CSS variables\nconst customStyle = computed(() => {\n if (!isCustomColor.value) return undefined\n return {\n '--chip-bg': props.tone + '22',\n '--chip-color': props.tone,\n }\n})\n\nconst toneClasses = computed(() => {\n if (isCustomColor.value) {\n return 'border border-transparent bg-[var(--chip-bg)] text-[var(--chip-color)]'\n }\n if (props.tone === 'neutral' && !props.selected) {\n return 'border border-outline bg-transparent text-on-surface-variant'\n }\n const map: Record<string, string> = {\n neutral: 'border border-transparent bg-secondary-container text-on-secondary-container',\n primary: 'border border-transparent bg-primary-container text-on-primary-container',\n secondary: 'border border-transparent bg-secondary-container text-on-secondary-container',\n success: 'border border-transparent bg-success-container text-on-success-container',\n error: 'border border-transparent bg-error-container text-on-error-container',\n tertiary: 'border border-transparent bg-tertiary-container text-on-tertiary-container',\n }\n return map[props.tone ?? 'neutral'] ?? map.neutral\n})\n</script>\n\n<template>\n <component\n :is=\"clickable ? 'button' : 'span'\"\n :type=\"clickable ? 'button' : undefined\"\n :disabled=\"clickable && disabled ? true : undefined\"\n class=\"inline-flex h-8 items-center gap-1.5 rounded-sm px-3 text-label-large transition-colors\"\n :class=\"[\n toneClasses,\n clickable && !disabled ? 'cursor-pointer hover:bg-on-surface/8' : '',\n disabled ? 'cursor-not-allowed opacity-[0.38]' : '',\n ]\"\n :style=\"customStyle\"\n @click=\"clickable && !disabled && emit('click')\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"18\" />\n <slot />\n <button\n v-if=\"removable\"\n type=\"button\"\n class=\"-mr-1 ml-0.5 inline-flex items-center justify-center rounded-full hover:bg-on-surface/12\"\n aria-label=\"Quitar\"\n :disabled=\"disabled\"\n @click.stop=\"emit('remove')\"\n >\n <MIcon name=\"close\" :size=\"16\" />\n </button>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst NAMED_TONES = ['neutral', 'primary', 'success', 'error', 'tertiary', 'secondary'] as const\ntype NamedTone = (typeof NAMED_TONES)[number]\n\nconst props = withDefaults(\n defineProps<{\n tone?: string // named tone OR CSS color string\n selected?: boolean\n removable?: boolean\n clickable?: boolean\n disabled?: boolean\n icon?: string\n }>(),\n {\n tone: 'neutral',\n selected: false,\n removable: false,\n clickable: false,\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{ click: []; remove: [] }>()\n\nconst isCustomColor = computed(\n () => !!props.tone && !(NAMED_TONES as readonly string[]).includes(props.tone),\n)\n\n// When a CSS color is passed, apply it via CSS variables\nconst customStyle = computed(() => {\n if (!isCustomColor.value) return undefined\n return {\n '--chip-bg': props.tone + '22',\n '--chip-color': props.tone,\n }\n})\n\nconst toneClasses = computed(() => {\n if (isCustomColor.value) {\n return 'border border-transparent bg-[var(--chip-bg)] text-[var(--chip-color)]'\n }\n if (props.tone === 'neutral' && !props.selected) {\n return 'border border-outline bg-transparent text-on-surface-variant'\n }\n const map: Record<string, string> = {\n neutral: 'border border-transparent bg-secondary-container text-on-secondary-container',\n primary: 'border border-transparent bg-primary-container text-on-primary-container',\n secondary: 'border border-transparent bg-secondary-container text-on-secondary-container',\n success: 'border border-transparent bg-success-container text-on-success-container',\n error: 'border border-transparent bg-error-container text-on-error-container',\n tertiary: 'border border-transparent bg-tertiary-container text-on-tertiary-container',\n }\n return map[props.tone ?? 'neutral'] ?? map.neutral\n})\n</script>\n\n<template>\n <component\n :is=\"clickable ? 'button' : 'span'\"\n :type=\"clickable ? 'button' : undefined\"\n :disabled=\"clickable && disabled ? true : undefined\"\n class=\"inline-flex h-8 items-center gap-1.5 rounded-sm px-3 text-label-large transition-colors\"\n :class=\"[\n toneClasses,\n clickable && !disabled ? 'cursor-pointer hover:bg-on-surface/8' : '',\n disabled ? 'cursor-not-allowed opacity-[0.38]' : '',\n ]\"\n :style=\"customStyle\"\n @click=\"clickable && !disabled && emit('click')\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"18\" />\n <slot />\n <button\n v-if=\"removable\"\n type=\"button\"\n class=\"-mr-1 ml-0.5 inline-flex items-center justify-center rounded-full hover:bg-on-surface/12\"\n aria-label=\"Quitar\"\n :disabled=\"disabled\"\n @click.stop=\"emit('remove')\"\n >\n <MIcon name=\"close\" :size=\"16\" />\n </button>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string\n label?: string\n presets?: string[]\n disabled?: boolean\n error?: string\n hint?: string\n fieldBg?: string\n}>(), {\n presets: () => [\n '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3',\n '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39',\n '#ffeb3b', '#ffc107', '#ff9800', '#ff5722', '#795548', '#607d8b',\n ],\n})\n\nconst emit = defineEmits<{ 'update:modelValue': [string] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst satBrightEl = ref<HTMLElement>()\nconst draggingSB = ref(false)\n\n// HSV state\nconst hue = ref(0)\nconst sat = ref(100)\nconst bright = ref(100)\n\nfunction hexToHsv(hex: string) {\n let c = hex.replace('#', '')\n if (c.length === 3) c = c[0]!+c[0]!+c[1]!+c[1]!+c[2]!+c[2]!\n const r = parseInt(c.substring(0, 2), 16) / 255\n const g = parseInt(c.substring(2, 4), 16) / 255\n const b = parseInt(c.substring(4, 6), 16) / 255\n const max = Math.max(r, g, b), min = Math.min(r, g, b)\n const d = max - min\n let h = 0\n if (d !== 0) {\n if (max === r) h = ((g - b) / d + 6) % 6\n else if (max === g) h = (b - r) / d + 2\n else h = (r - g) / d + 4\n h *= 60\n }\n const s = max === 0 ? 0 : (d / max) * 100\n const v = max * 100\n return { h, s, v }\n}\n\nfunction hsvToHex(h: number, s: number, v: number) {\n s /= 100; v /= 100\n const c = v * s\n const x = c * (1 - Math.abs((h / 60) % 2 - 1))\n const m = v - c\n let r = 0, g = 0, b = 0\n if (h < 60) { r = c; g = x }\n else if (h < 120) { r = x; g = c }\n else if (h < 180) { g = c; b = x }\n else if (h < 240) { g = x; b = c }\n else if (h < 300) { r = x; b = c }\n else { r = c; b = x }\n const toHex = (n: number) => Math.round((n + m) * 255).toString(16).padStart(2, '0')\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`\n}\n\nfunction syncFromProp() {\n const hsv = hexToHsv(props.modelValue)\n hue.value = hsv.h\n sat.value = hsv.s\n bright.value = hsv.v\n}\nsyncFromProp()\n\nwatch(() => props.modelValue, syncFromProp)\n\nfunction emitColor() {\n emit('update:modelValue', hsvToHex(hue.value, sat.value, bright.value))\n}\n\nconst currentHex = computed(() => hsvToHex(hue.value, sat.value, bright.value))\nconst hueColor = computed(() => `hsl(${hue.value}, 100%, 50%)`)\n\nfunction onSBPointerDown(e: PointerEvent) {\n draggingSB.value = true\n updateSB(e)\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onSBPointerMove(e: PointerEvent) {\n if (!draggingSB.value) return\n updateSB(e)\n}\nfunction onSBPointerUp() {\n draggingSB.value = false\n emitColor()\n}\nfunction updateSB(e: PointerEvent) {\n if (!satBrightEl.value) return\n const rect = satBrightEl.value.getBoundingClientRect()\n sat.value = Math.max(0, Math.min(100, ((e.clientX - rect.left) / rect.width) * 100))\n bright.value = Math.max(0, Math.min(100, (1 - (e.clientY - rect.top) / rect.height) * 100))\n}\n\nfunction onHueInput(e: Event) {\n hue.value = Number((e.target as HTMLInputElement).value)\n emitColor()\n}\n\nfunction selectPreset(color: string) {\n emit('update:modelValue', color)\n}\n\nfunction onHexInput(e: Event) {\n const v = (e.target as HTMLInputElement).value\n if (/^#[0-9a-fA-F]{6}$/.test(v)) {\n emit('update:modelValue', v)\n }\n}\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 380\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-3 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <span\n class=\"h-6 w-6 shrink-0 rounded-full border border-outline-variant\"\n :style=\"{ backgroundColor: modelValue }\"\n />\n <span class=\"flex-1 font-mono text-on-surface\">{{ modelValue }}</span>\n <MIcon name=\"palette\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[280px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Saturation / Brightness area -->\n <div\n ref=\"satBrightEl\"\n class=\"relative mb-3 h-40 w-full cursor-crosshair overflow-hidden rounded-lg\"\n :style=\"{ background: `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${hueColor})` }\"\n @pointerdown=\"onSBPointerDown\"\n @pointermove=\"onSBPointerMove\"\n @pointerup=\"onSBPointerUp\"\n >\n <div\n class=\"pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-elevation-1\"\n :style=\"{\n left: `${sat}%`,\n top: `${100 - bright}%`,\n backgroundColor: currentHex,\n }\"\n />\n </div>\n\n <!-- Hue slider -->\n <div class=\"mb-3\">\n <input\n type=\"range\"\n min=\"0\"\n max=\"360\"\n :value=\"hue\"\n class=\"hue-slider h-3 w-full cursor-pointer appearance-none rounded-full outline-none\"\n @input=\"onHueInput\"\n />\n </div>\n\n <!-- Preview + hex input -->\n <div class=\"mb-3 flex items-center gap-3\">\n <span\n class=\"h-9 w-9 shrink-0 rounded-full border border-outline-variant\"\n :style=\"{ backgroundColor: currentHex }\"\n />\n <input\n type=\"text\"\n :value=\"modelValue\"\n maxlength=\"7\"\n class=\"flex-1 rounded-sm border border-outline bg-transparent px-3 py-2 font-mono text-body-medium text-on-surface outline-none transition-colors focus:border-primary\"\n @input=\"onHexInput\"\n />\n </div>\n\n <!-- Presets -->\n <div class=\"flex flex-wrap gap-1.5\">\n <button\n v-for=\"color in presets\"\n :key=\"color\"\n type=\"button\"\n class=\"flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border transition-transform duration-100 hover:scale-110\"\n :class=\"color === modelValue ? 'border-on-surface' : 'border-transparent'\"\n :style=\"{ backgroundColor: color }\"\n @click=\"selectPreset(color)\"\n >\n <MIcon v-if=\"color === modelValue\" name=\"check\" :size=\"14\" class=\"text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.5)]\" />\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<style scoped>\n.hue-slider {\n background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);\n}\n.hue-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n cursor: pointer;\n}\n.hue-slider::-moz-range-thumb {\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n border: none;\n cursor: pointer;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string\n label?: string\n presets?: string[]\n disabled?: boolean\n error?: string\n hint?: string\n fieldBg?: string\n}>(), {\n presets: () => [\n '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3',\n '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39',\n '#ffeb3b', '#ffc107', '#ff9800', '#ff5722', '#795548', '#607d8b',\n ],\n})\n\nconst emit = defineEmits<{ 'update:modelValue': [string] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst satBrightEl = ref<HTMLElement>()\nconst draggingSB = ref(false)\n\n// HSV state\nconst hue = ref(0)\nconst sat = ref(100)\nconst bright = ref(100)\n\nfunction hexToHsv(hex: string) {\n let c = hex.replace('#', '')\n if (c.length === 3) c = c[0]!+c[0]!+c[1]!+c[1]!+c[2]!+c[2]!\n const r = parseInt(c.substring(0, 2), 16) / 255\n const g = parseInt(c.substring(2, 4), 16) / 255\n const b = parseInt(c.substring(4, 6), 16) / 255\n const max = Math.max(r, g, b), min = Math.min(r, g, b)\n const d = max - min\n let h = 0\n if (d !== 0) {\n if (max === r) h = ((g - b) / d + 6) % 6\n else if (max === g) h = (b - r) / d + 2\n else h = (r - g) / d + 4\n h *= 60\n }\n const s = max === 0 ? 0 : (d / max) * 100\n const v = max * 100\n return { h, s, v }\n}\n\nfunction hsvToHex(h: number, s: number, v: number) {\n s /= 100; v /= 100\n const c = v * s\n const x = c * (1 - Math.abs((h / 60) % 2 - 1))\n const m = v - c\n let r = 0, g = 0, b = 0\n if (h < 60) { r = c; g = x }\n else if (h < 120) { r = x; g = c }\n else if (h < 180) { g = c; b = x }\n else if (h < 240) { g = x; b = c }\n else if (h < 300) { r = x; b = c }\n else { r = c; b = x }\n const toHex = (n: number) => Math.round((n + m) * 255).toString(16).padStart(2, '0')\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`\n}\n\nfunction syncFromProp() {\n const hsv = hexToHsv(props.modelValue)\n hue.value = hsv.h\n sat.value = hsv.s\n bright.value = hsv.v\n}\nsyncFromProp()\n\nwatch(() => props.modelValue, syncFromProp)\n\nfunction emitColor() {\n emit('update:modelValue', hsvToHex(hue.value, sat.value, bright.value))\n}\n\nconst currentHex = computed(() => hsvToHex(hue.value, sat.value, bright.value))\nconst hueColor = computed(() => `hsl(${hue.value}, 100%, 50%)`)\n\nfunction onSBPointerDown(e: PointerEvent) {\n draggingSB.value = true\n updateSB(e)\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onSBPointerMove(e: PointerEvent) {\n if (!draggingSB.value) return\n updateSB(e)\n}\nfunction onSBPointerUp() {\n draggingSB.value = false\n emitColor()\n}\nfunction updateSB(e: PointerEvent) {\n if (!satBrightEl.value) return\n const rect = satBrightEl.value.getBoundingClientRect()\n sat.value = Math.max(0, Math.min(100, ((e.clientX - rect.left) / rect.width) * 100))\n bright.value = Math.max(0, Math.min(100, (1 - (e.clientY - rect.top) / rect.height) * 100))\n}\n\nfunction onHueInput(e: Event) {\n hue.value = Number((e.target as HTMLInputElement).value)\n emitColor()\n}\n\nfunction selectPreset(color: string) {\n emit('update:modelValue', color)\n}\n\nfunction onHexInput(e: Event) {\n const v = (e.target as HTMLInputElement).value\n if (/^#[0-9a-fA-F]{6}$/.test(v)) {\n emit('update:modelValue', v)\n }\n}\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 380\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-3 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <span\n class=\"h-6 w-6 shrink-0 rounded-full border border-outline-variant\"\n :style=\"{ backgroundColor: modelValue }\"\n />\n <span class=\"flex-1 font-mono text-on-surface\">{{ modelValue }}</span>\n <MIcon name=\"palette\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[280px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Saturation / Brightness area -->\n <div\n ref=\"satBrightEl\"\n class=\"relative mb-3 h-40 w-full cursor-crosshair overflow-hidden rounded-lg\"\n :style=\"{ background: `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${hueColor})` }\"\n @pointerdown=\"onSBPointerDown\"\n @pointermove=\"onSBPointerMove\"\n @pointerup=\"onSBPointerUp\"\n >\n <div\n class=\"pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-elevation-1\"\n :style=\"{\n left: `${sat}%`,\n top: `${100 - bright}%`,\n backgroundColor: currentHex,\n }\"\n />\n </div>\n\n <!-- Hue slider -->\n <div class=\"mb-3\">\n <input\n type=\"range\"\n min=\"0\"\n max=\"360\"\n :value=\"hue\"\n class=\"hue-slider h-3 w-full cursor-pointer appearance-none rounded-full outline-none\"\n @input=\"onHueInput\"\n />\n </div>\n\n <!-- Preview + hex input -->\n <div class=\"mb-3 flex items-center gap-3\">\n <span\n class=\"h-9 w-9 shrink-0 rounded-full border border-outline-variant\"\n :style=\"{ backgroundColor: currentHex }\"\n />\n <input\n type=\"text\"\n :value=\"modelValue\"\n maxlength=\"7\"\n class=\"flex-1 rounded-sm border border-outline bg-transparent px-3 py-2 font-mono text-body-medium text-on-surface outline-none transition-colors focus:border-primary\"\n @input=\"onHexInput\"\n />\n </div>\n\n <!-- Presets -->\n <div class=\"flex flex-wrap gap-1.5\">\n <button\n v-for=\"color in presets\"\n :key=\"color\"\n type=\"button\"\n class=\"flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border transition-transform duration-100 hover:scale-110\"\n :class=\"color === modelValue ? 'border-on-surface' : 'border-transparent'\"\n :style=\"{ backgroundColor: color }\"\n @click=\"selectPreset(color)\"\n >\n <MIcon v-if=\"color === modelValue\" name=\"check\" :size=\"14\" class=\"text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.5)]\" />\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<style scoped>\n.hue-slider {\n background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);\n}\n.hue-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n cursor: pointer;\n}\n.hue-slider::-moz-range-thumb {\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n border: none;\n cursor: pointer;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface CommandItem {\n id: string\n label: string\n icon?: string\n shortcut?: string\n group?: string\n disabled?: boolean\n onSelect?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n items: CommandItem[]\n placeholder?: string\n noResultsText?: string\n hotkey?: string\n }>(),\n {\n placeholder: 'Buscar comando...',\n noResultsText: 'Sin resultados',\n hotkey: 'k',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n select: [CommandItem]\n}>()\n\nconst query = ref('')\nconst activeIndex = ref(0)\nconst inputRef = ref<HTMLInputElement | null>(null)\n\nconst filtered = computed(() => {\n if (!query.value) return props.items.filter(i => !i.disabled)\n const q = query.value.toLowerCase()\n return props.items.filter(\n i => !i.disabled && (i.label.toLowerCase().includes(q) || i.group?.toLowerCase().includes(q)),\n )\n})\n\nconst grouped = computed(() => {\n const map = new Map<string, CommandItem[]>()\n for (const item of filtered.value) {\n const g = item.group ?? ''\n if (!map.has(g)) map.set(g, [])\n map.get(g)!.push(item)\n }\n return map\n})\n\nfunction open() {\n emit('update:modelValue', true)\n}\n\nfunction close() {\n query.value = ''\n activeIndex.value = 0\n emit('update:modelValue', false)\n}\n\nfunction selectItem(item: CommandItem) {\n emit('select', item)\n item.onSelect?.()\n close()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n activeIndex.value = (activeIndex.value + 1) % filtered.value.length\n scrollToActive()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n activeIndex.value = (activeIndex.value - 1 + filtered.value.length) % filtered.value.length\n scrollToActive()\n } else if (e.key === 'Enter' && filtered.value.length) {\n e.preventDefault()\n selectItem(filtered.value[activeIndex.value]!)\n } else if (e.key === 'Escape') {\n close()\n }\n}\n\nfunction scrollToActive() {\n nextTick(() => {\n const el = document.querySelector('[data-cmd-active=\"true\"]')\n el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction onGlobalKeydown(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === props.hotkey) {\n e.preventDefault()\n if (props.modelValue) close()\n else open()\n }\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.body.style.overflow = 'hidden'\n nextTick(() => inputRef.value?.focus())\n } else {\n document.body.style.overflow = ''\n }\n },\n)\n\nwatch(query, () => { activeIndex.value = 0 })\n\nonMounted(() => document.addEventListener('keydown', onGlobalKeydown))\nonBeforeUnmount(() => document.removeEventListener('keydown', onGlobalKeydown))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-cmd\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-black/40 pt-[15vh]\"\n @click.self=\"close\"\n >\n <div class=\"cmd-box flex w-full max-w-lg flex-col overflow-hidden rounded-xl bg-surface-container-high shadow-elevation-3\">\n <!-- Search input -->\n <div class=\"flex items-center gap-3 border-b border-outline-variant px-4\">\n <MIcon name=\"search\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n ref=\"inputRef\"\n v-model=\"query\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"h-12 flex-1 bg-transparent text-body-large text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n @keydown=\"onKeydown\"\n />\n <kbd class=\"rounded bg-surface-container px-1.5 py-0.5 text-label-small text-on-surface-variant\">\n ESC\n </kbd>\n </div>\n\n <!-- Results -->\n <div class=\"max-h-80 overflow-y-auto py-2\">\n <template v-if=\"filtered.length\">\n <template v-for=\"[group, items] in grouped\" :key=\"group\">\n <p v-if=\"group\" class=\"px-4 pt-3 pb-1 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ group }}\n </p>\n <button\n v-for=\"(item, i) in items\"\n :key=\"item.id\"\n type=\"button\"\n :data-cmd-active=\"filtered.indexOf(item) === activeIndex || undefined\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left transition-colors\"\n :class=\"filtered.indexOf(item) === activeIndex ? 'bg-primary/12 text-primary' : 'text-on-surface hover:bg-on-surface/4'\"\n @click=\"selectItem(item)\"\n @pointerenter=\"activeIndex = filtered.indexOf(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"20\" class=\"shrink-0 opacity-70\" />\n <span class=\"flex-1 truncate text-body-medium\">{{ item.label }}</span>\n <kbd v-if=\"item.shortcut\" class=\"rounded bg-surface-container px-1.5 py-0.5 text-label-small text-on-surface-variant\">\n {{ item.shortcut }}\n </kbd>\n </button>\n </template>\n </template>\n <p v-else class=\"px-4 py-6 text-center text-body-medium text-on-surface-variant\">\n {{ noResultsText }}\n </p>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center gap-4 border-t border-outline-variant px-4 py-2\">\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↑↓</kbd> navegar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↵</kbd> seleccionar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">esc</kbd> cerrar\n </span>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-cmd-enter-active,\n.m3-cmd-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-cmd-enter-from,\n.m3-cmd-leave-to {\n opacity: 0;\n}\n.m3-cmd-enter-active .cmd-box,\n.m3-cmd-leave-active .cmd-box {\n transition: transform 0.15s ease;\n}\n.m3-cmd-enter-from .cmd-box,\n.m3-cmd-leave-to .cmd-box {\n transform: scale(0.95) translateY(-10px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface CommandItem {\n id: string\n label: string\n icon?: string\n shortcut?: string\n group?: string\n disabled?: boolean\n onSelect?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n items: CommandItem[]\n placeholder?: string\n noResultsText?: string\n hotkey?: string\n }>(),\n {\n placeholder: 'Buscar comando...',\n noResultsText: 'Sin resultados',\n hotkey: 'k',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n select: [CommandItem]\n}>()\n\nconst query = ref('')\nconst activeIndex = ref(0)\nconst inputRef = ref<HTMLInputElement | null>(null)\n\nconst filtered = computed(() => {\n if (!query.value) return props.items.filter(i => !i.disabled)\n const q = query.value.toLowerCase()\n return props.items.filter(\n i => !i.disabled && (i.label.toLowerCase().includes(q) || i.group?.toLowerCase().includes(q)),\n )\n})\n\nconst grouped = computed(() => {\n const map = new Map<string, CommandItem[]>()\n for (const item of filtered.value) {\n const g = item.group ?? ''\n if (!map.has(g)) map.set(g, [])\n map.get(g)!.push(item)\n }\n return map\n})\n\nfunction open() {\n emit('update:modelValue', true)\n}\n\nfunction close() {\n query.value = ''\n activeIndex.value = 0\n emit('update:modelValue', false)\n}\n\nfunction selectItem(item: CommandItem) {\n emit('select', item)\n item.onSelect?.()\n close()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n activeIndex.value = (activeIndex.value + 1) % filtered.value.length\n scrollToActive()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n activeIndex.value = (activeIndex.value - 1 + filtered.value.length) % filtered.value.length\n scrollToActive()\n } else if (e.key === 'Enter' && filtered.value.length) {\n e.preventDefault()\n selectItem(filtered.value[activeIndex.value]!)\n } else if (e.key === 'Escape') {\n close()\n }\n}\n\nfunction scrollToActive() {\n nextTick(() => {\n const el = document.querySelector('[data-cmd-active=\"true\"]')\n el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction onGlobalKeydown(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === props.hotkey) {\n e.preventDefault()\n if (props.modelValue) close()\n else open()\n }\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.body.style.overflow = 'hidden'\n nextTick(() => inputRef.value?.focus())\n } else {\n document.body.style.overflow = ''\n }\n },\n)\n\nwatch(query, () => { activeIndex.value = 0 })\n\nonMounted(() => document.addEventListener('keydown', onGlobalKeydown))\nonBeforeUnmount(() => document.removeEventListener('keydown', onGlobalKeydown))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-cmd\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-black/40 pt-[15vh]\"\n @click.self=\"close\"\n >\n <div class=\"cmd-box flex w-full max-w-lg flex-col overflow-hidden rounded-xl bg-surface-container-high shadow-elevation-3\">\n <!-- Search input -->\n <div class=\"flex items-center gap-3 border-b border-outline-variant px-4\">\n <MIcon name=\"search\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n ref=\"inputRef\"\n v-model=\"query\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"h-12 flex-1 bg-transparent text-body-large text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n @keydown=\"onKeydown\"\n />\n <kbd class=\"rounded bg-surface-container px-1.5 py-0.5 text-label-small text-on-surface-variant\">\n ESC\n </kbd>\n </div>\n\n <!-- Results -->\n <div class=\"max-h-80 overflow-y-auto py-2\">\n <template v-if=\"filtered.length\">\n <template v-for=\"[group, items] in grouped\" :key=\"group\">\n <p v-if=\"group\" class=\"px-4 pt-3 pb-1 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ group }}\n </p>\n <button\n v-for=\"(item, i) in items\"\n :key=\"item.id\"\n type=\"button\"\n :data-cmd-active=\"filtered.indexOf(item) === activeIndex || undefined\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left transition-colors\"\n :class=\"filtered.indexOf(item) === activeIndex ? 'bg-primary/12 text-primary' : 'text-on-surface hover:bg-on-surface/4'\"\n @click=\"selectItem(item)\"\n @pointerenter=\"activeIndex = filtered.indexOf(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"20\" class=\"shrink-0 opacity-70\" />\n <span class=\"flex-1 truncate text-body-medium\">{{ item.label }}</span>\n <kbd v-if=\"item.shortcut\" class=\"rounded bg-surface-container px-1.5 py-0.5 text-label-small text-on-surface-variant\">\n {{ item.shortcut }}\n </kbd>\n </button>\n </template>\n </template>\n <p v-else class=\"px-4 py-6 text-center text-body-medium text-on-surface-variant\">\n {{ noResultsText }}\n </p>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center gap-4 border-t border-outline-variant px-4 py-2\">\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↑↓</kbd> navegar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↵</kbd> seleccionar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">esc</kbd> cerrar\n </span>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-cmd-enter-active,\n.m3-cmd-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-cmd-enter-from,\n.m3-cmd-leave-to {\n opacity: 0;\n}\n.m3-cmd-enter-active .cmd-box,\n.m3-cmd-leave-active .cmd-box {\n transition: transform 0.15s ease;\n}\n.m3-cmd-enter-from .cmd-box,\n.m3-cmd-leave-to .cmd-box {\n transform: scale(0.95) translateY(-10px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n title?: string\n maxWidth?: string\n persistent?: boolean\n }>(),\n {\n maxWidth: 'max-w-md',\n persistent: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\nfunction close() {\n if (props.persistent) return\n emit('update:modelValue', false)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') close()\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.addEventListener('keydown', onKeydown)\n document.body.style.overflow = 'hidden'\n } else {\n document.removeEventListener('keydown', onKeydown)\n document.body.style.overflow = ''\n }\n },\n)\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-dialog\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4\"\n @click.self=\"close\"\n >\n <div\n class=\"dialog-box flex max-h-[90vh] w-full flex-col rounded-xl bg-surface-container-high shadow-elevation-3\"\n :class=\"maxWidth\"\n >\n <div class=\"flex items-start justify-between gap-4 px-6 pt-6 pb-2\">\n <h2 class=\"text-headline-small text-on-surface\">\n <slot name=\"title\">{{ title }}</slot>\n </h2>\n <MIconButton v-if=\"!persistent\" icon=\"close\" label=\"Cerrar\" @click=\"close\" />\n </div>\n <div class=\"overflow-y-auto px-6 py-2 text-body-medium text-on-surface-variant\">\n <slot />\n </div>\n <div v-if=\"$slots.actions\" class=\"flex justify-end gap-2 px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-dialog-enter-active,\n.m3-dialog-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-dialog-enter-from,\n.m3-dialog-leave-to {\n opacity: 0;\n}\n.m3-dialog-enter-active .dialog-box,\n.m3-dialog-leave-active .dialog-box {\n transition: transform 0.15s ease;\n}\n.m3-dialog-enter-from .dialog-box,\n.m3-dialog-leave-to .dialog-box {\n transform: scale(0.95);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n title?: string\n maxWidth?: string\n persistent?: boolean\n }>(),\n {\n maxWidth: 'max-w-md',\n persistent: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\nfunction close() {\n if (props.persistent) return\n emit('update:modelValue', false)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') close()\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.addEventListener('keydown', onKeydown)\n document.body.style.overflow = 'hidden'\n } else {\n document.removeEventListener('keydown', onKeydown)\n document.body.style.overflow = ''\n }\n },\n)\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-dialog\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4\"\n @click.self=\"close\"\n >\n <div\n class=\"dialog-box flex max-h-[90vh] w-full flex-col rounded-xl bg-surface-container-high shadow-elevation-3\"\n :class=\"maxWidth\"\n >\n <div class=\"flex items-start justify-between gap-4 px-6 pt-6 pb-2\">\n <h2 class=\"text-headline-small text-on-surface\">\n <slot name=\"title\">{{ title }}</slot>\n </h2>\n <MIconButton v-if=\"!persistent\" icon=\"close\" label=\"Cerrar\" @click=\"close\" />\n </div>\n <div class=\"overflow-y-auto px-6 py-2 text-body-medium text-on-surface-variant\">\n <slot />\n </div>\n <div v-if=\"$slots.actions\" class=\"flex justify-end gap-2 px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-dialog-enter-active,\n.m3-dialog-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-dialog-enter-from,\n.m3-dialog-leave-to {\n opacity: 0;\n}\n.m3-dialog-enter-active .dialog-box,\n.m3-dialog-leave-active .dialog-box {\n transition: transform 0.15s ease;\n}\n.m3-dialog-enter-from .dialog-box,\n.m3-dialog-leave-to .dialog-box {\n transform: scale(0.95);\n}\n</style>\n","<script setup lang=\"ts\">\nimport MDialog from './MDialog.vue'\nimport MButton from './MButton.vue'\n\nwithDefaults(\n defineProps<{\n modelValue: boolean\n title: string\n message: string\n confirmLabel?: string\n cancelLabel?: string\n danger?: boolean\n loading?: boolean\n }>(),\n {\n confirmLabel: 'Confirmar',\n cancelLabel: 'Cancelar',\n danger: false,\n loading: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean]; confirm: [] }>()\n</script>\n\n<template>\n <MDialog\n :model-value=\"modelValue\"\n :title=\"title\"\n max-width=\"max-w-sm\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <p class=\"text-body-medium text-on-surface-variant\">{{ message }}</p>\n <template #actions>\n <MButton variant=\"text\" :disabled=\"loading\" @click=\"emit('update:modelValue', false)\">\n {{ cancelLabel }}\n </MButton>\n <MButton :color=\"danger ? 'error' : 'primary'\" :loading=\"loading\" @click=\"emit('confirm')\">\n {{ confirmLabel }}\n </MButton>\n </template>\n </MDialog>\n</template>\n","<script setup lang=\"ts\">\nimport MDialog from './MDialog.vue'\nimport MButton from './MButton.vue'\n\nwithDefaults(\n defineProps<{\n modelValue: boolean\n title: string\n message: string\n confirmLabel?: string\n cancelLabel?: string\n danger?: boolean\n loading?: boolean\n }>(),\n {\n confirmLabel: 'Confirmar',\n cancelLabel: 'Cancelar',\n danger: false,\n loading: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean]; confirm: [] }>()\n</script>\n\n<template>\n <MDialog\n :model-value=\"modelValue\"\n :title=\"title\"\n max-width=\"max-w-sm\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <p class=\"text-body-medium text-on-surface-variant\">{{ message }}</p>\n <template #actions>\n <MButton variant=\"text\" :disabled=\"loading\" @click=\"emit('update:modelValue', false)\">\n {{ cancelLabel }}\n </MButton>\n <MButton :color=\"danger ? 'error' : 'primary'\" :loading=\"loading\" @click=\"emit('confirm')\">\n {{ confirmLabel }}\n </MButton>\n </template>\n </MDialog>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\n fluid?: boolean\n centered?: boolean\n padding?: boolean\n }>(),\n { maxWidth: 'lg', fluid: false, centered: true, padding: true },\n)\n\nconst maxWidthClasses: Record<string, string> = {\n xs: 'max-w-screen-xs',\n sm: 'max-w-screen-sm',\n md: 'max-w-screen-md',\n lg: 'max-w-screen-lg',\n xl: 'max-w-screen-xl',\n '2xl': 'max-w-screen-2xl',\n full: 'max-w-full',\n}\n\nconst classes = computed(() => [\n 'w-full',\n props.fluid ? 'max-w-full' : maxWidthClasses[props.maxWidth],\n props.centered && 'mx-auto',\n props.padding && 'px-4 sm:px-6 lg:px-8',\n])\n</script>\n\n<template>\n <div :class=\"classes\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\n fluid?: boolean\n centered?: boolean\n padding?: boolean\n }>(),\n { maxWidth: 'lg', fluid: false, centered: true, padding: true },\n)\n\nconst maxWidthClasses: Record<string, string> = {\n xs: 'max-w-screen-xs',\n sm: 'max-w-screen-sm',\n md: 'max-w-screen-md',\n lg: 'max-w-screen-lg',\n xl: 'max-w-screen-xl',\n '2xl': 'max-w-screen-2xl',\n full: 'max-w-full',\n}\n\nconst classes = computed(() => [\n 'w-full',\n props.fluid ? 'max-w-full' : maxWidthClasses[props.maxWidth],\n props.centered && 'mx-auto',\n props.padding && 'px-4 sm:px-6 lg:px-8',\n])\n</script>\n\n<template>\n <div :class=\"classes\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MContextMenuPanel from './_MContextMenuPanel.vue'\nimport type { ContextMenuItem } from './MContextMenu.vue'\n\nconst props = defineProps<{\n items: ContextMenuItem[]\n x: number\n y: number\n}>()\n\nconst emit = defineEmits<{ close: [] }>()\n\nconst panel = ref<HTMLElement | null>(null)\nconst panelX = ref(props.x)\nconst panelY = ref(props.y)\nconst activeIndex = ref<number | null>(null)\nconst subPos = ref({ x: 0, y: 0 })\n\nonMounted(async () => {\n await nextTick()\n if (!panel.value) return\n const el = panel.value\n panelX.value = Math.min(props.x, window.innerWidth - el.offsetWidth - 8)\n panelY.value = Math.min(props.y, window.innerHeight - el.offsetHeight - 8)\n})\n\nfunction onItemMouseEnter(index: number, item: ContextMenuItem, e: MouseEvent) {\n if (item.divider || item.disabled) {\n activeIndex.value = null\n return\n }\n if (!item.children?.length) {\n activeIndex.value = null\n return\n }\n\n activeIndex.value = index\n const itemEl = e.currentTarget as HTMLElement\n const itemRect = itemEl.getBoundingClientRect()\n const panelRect = panel.value!.getBoundingClientRect()\n\n let x = panelRect.right\n let y = itemRect.top\n if (x + 220 > window.innerWidth) x = panelRect.left - 220\n if (y + 300 > window.innerHeight) y = Math.max(8, window.innerHeight - 300)\n\n subPos.value = { x, y }\n}\n\nfunction onItemClick(item: ContextMenuItem) {\n if (item.disabled || item.divider || item.children?.length) return\n item.onClick?.()\n emit('close')\n}\n\nfunction onPanelMouseLeave(e: MouseEvent) {\n // Don't close if the mouse moved to another context menu panel (sibling sub-panel)\n const related = e.relatedTarget as Element | null\n if (related?.closest('.m3-ctx-panel')) return\n activeIndex.value = null\n}\n</script>\n\n<template>\n <div\n ref=\"panel\"\n class=\"m3-ctx-panel absolute z-[201] min-w-[200px] rounded-sm bg-surface-container shadow-elevation-2\"\n :style=\"{ left: `${panelX}px`, top: `${panelY}px` }\"\n @mouseleave=\"onPanelMouseLeave\"\n >\n <div class=\"overflow-hidden rounded-sm py-1\">\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <hr v-if=\"item.divider\" class=\"my-1 border-outline-variant\" />\n\n <div\n v-else\n class=\"relative flex cursor-default select-none items-center gap-3 px-4 py-2.5 text-body-large\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-38 text-on-surface'\n : item.danger\n ? 'cursor-pointer text-error hover:bg-error/8'\n : 'cursor-pointer text-on-surface hover:bg-on-surface/8',\n activeIndex === i && !item.disabled\n ? (item.danger ? 'bg-error/8' : 'bg-on-surface/8')\n : '',\n ]\"\n @mouseenter=\"onItemMouseEnter(i, item, $event)\"\n @click=\"onItemClick(item)\"\n >\n <MIcon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n :size=\"18\"\n class=\"shrink-0\"\n :class=\"item.danger ? 'text-error' : 'text-on-surface-variant'\"\n />\n <span v-else class=\"w-[18px] shrink-0\" />\n\n <span class=\"flex-1\">{{ item.label }}</span>\n\n <span v-if=\"item.shortcut\" class=\"text-label-small text-on-surface-variant\">\n {{ item.shortcut }}\n </span>\n\n <MIcon\n v-if=\"item.children?.length\"\n name=\"chevron_right\"\n :size=\"18\"\n class=\"shrink-0 text-on-surface-variant\"\n />\n </div>\n </template>\n </div>\n </div>\n\n <!-- Sub-panel: sibling in the same Teleport layer.\n No <Transition> wrapper — MContextMenuPanel is a fragment and cannot be\n animated by Vue's Transition (produces a console warning). -->\n <MContextMenuPanel\n v-if=\"activeIndex !== null && items[activeIndex]?.children?.length\"\n :items=\"items[activeIndex]!.children!\"\n :x=\"subPos.x\"\n :y=\"subPos.y\"\n @close=\"emit('close')\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MContextMenuPanel from './_MContextMenuPanel.vue'\nimport type { ContextMenuItem } from './MContextMenu.vue'\n\nconst props = defineProps<{\n items: ContextMenuItem[]\n x: number\n y: number\n}>()\n\nconst emit = defineEmits<{ close: [] }>()\n\nconst panel = ref<HTMLElement | null>(null)\nconst panelX = ref(props.x)\nconst panelY = ref(props.y)\nconst activeIndex = ref<number | null>(null)\nconst subPos = ref({ x: 0, y: 0 })\n\nonMounted(async () => {\n await nextTick()\n if (!panel.value) return\n const el = panel.value\n panelX.value = Math.min(props.x, window.innerWidth - el.offsetWidth - 8)\n panelY.value = Math.min(props.y, window.innerHeight - el.offsetHeight - 8)\n})\n\nfunction onItemMouseEnter(index: number, item: ContextMenuItem, e: MouseEvent) {\n if (item.divider || item.disabled) {\n activeIndex.value = null\n return\n }\n if (!item.children?.length) {\n activeIndex.value = null\n return\n }\n\n activeIndex.value = index\n const itemEl = e.currentTarget as HTMLElement\n const itemRect = itemEl.getBoundingClientRect()\n const panelRect = panel.value!.getBoundingClientRect()\n\n let x = panelRect.right\n let y = itemRect.top\n if (x + 220 > window.innerWidth) x = panelRect.left - 220\n if (y + 300 > window.innerHeight) y = Math.max(8, window.innerHeight - 300)\n\n subPos.value = { x, y }\n}\n\nfunction onItemClick(item: ContextMenuItem) {\n if (item.disabled || item.divider || item.children?.length) return\n item.onClick?.()\n emit('close')\n}\n\nfunction onPanelMouseLeave(e: MouseEvent) {\n // Don't close if the mouse moved to another context menu panel (sibling sub-panel)\n const related = e.relatedTarget as Element | null\n if (related?.closest('.m3-ctx-panel')) return\n activeIndex.value = null\n}\n</script>\n\n<template>\n <div\n ref=\"panel\"\n class=\"m3-ctx-panel absolute z-[201] min-w-[200px] rounded-sm bg-surface-container shadow-elevation-2\"\n :style=\"{ left: `${panelX}px`, top: `${panelY}px` }\"\n @mouseleave=\"onPanelMouseLeave\"\n >\n <div class=\"overflow-hidden rounded-sm py-1\">\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <hr v-if=\"item.divider\" class=\"my-1 border-outline-variant\" />\n\n <div\n v-else\n class=\"relative flex cursor-default select-none items-center gap-3 px-4 py-2.5 text-body-large\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-38 text-on-surface'\n : item.danger\n ? 'cursor-pointer text-error hover:bg-error/8'\n : 'cursor-pointer text-on-surface hover:bg-on-surface/8',\n activeIndex === i && !item.disabled\n ? (item.danger ? 'bg-error/8' : 'bg-on-surface/8')\n : '',\n ]\"\n @mouseenter=\"onItemMouseEnter(i, item, $event)\"\n @click=\"onItemClick(item)\"\n >\n <MIcon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n :size=\"18\"\n class=\"shrink-0\"\n :class=\"item.danger ? 'text-error' : 'text-on-surface-variant'\"\n />\n <span v-else class=\"w-[18px] shrink-0\" />\n\n <span class=\"flex-1\">{{ item.label }}</span>\n\n <span v-if=\"item.shortcut\" class=\"text-label-small text-on-surface-variant\">\n {{ item.shortcut }}\n </span>\n\n <MIcon\n v-if=\"item.children?.length\"\n name=\"chevron_right\"\n :size=\"18\"\n class=\"shrink-0 text-on-surface-variant\"\n />\n </div>\n </template>\n </div>\n </div>\n\n <!-- Sub-panel: sibling in the same Teleport layer.\n No <Transition> wrapper — MContextMenuPanel is a fragment and cannot be\n animated by Vue's Transition (produces a console warning). -->\n <MContextMenuPanel\n v-if=\"activeIndex !== null && items[activeIndex]?.children?.length\"\n :items=\"items[activeIndex]!.children!\"\n :x=\"subPos.x\"\n :y=\"subPos.y\"\n @close=\"emit('close')\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport MContextMenuPanel from './_MContextMenuPanel.vue'\n\nexport interface ContextMenuItem {\n label?: string\n icon?: string\n shortcut?: string\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n children?: ContextMenuItem[]\n onClick?: () => void\n}\n\ndefineProps<{ items: ContextMenuItem[] }>()\n\nconst visible = ref(false)\nconst position = ref({ x: 0, y: 0 })\n\nfunction show(e: MouseEvent) {\n e.preventDefault()\n e.stopPropagation()\n showAt(e.clientX, e.clientY)\n}\n\nfunction showAt(x: number, y: number) {\n position.value = { x, y }\n visible.value = true\n}\n\nfunction hide() {\n visible.value = false\n}\n\ndefineExpose({ show, showAt, hide })\n</script>\n\n<template>\n <slot :show=\"show\" />\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-100\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"fixed inset-0 z-[200]\"\n @mousedown.self=\"hide\"\n @contextmenu.prevent\n >\n <MContextMenuPanel\n :items=\"items\"\n :x=\"position.x\"\n :y=\"position.y\"\n @close=\"hide\"\n />\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport MContextMenuPanel from './_MContextMenuPanel.vue'\n\nexport interface ContextMenuItem {\n label?: string\n icon?: string\n shortcut?: string\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n children?: ContextMenuItem[]\n onClick?: () => void\n}\n\ndefineProps<{ items: ContextMenuItem[] }>()\n\nconst visible = ref(false)\nconst position = ref({ x: 0, y: 0 })\n\nfunction show(e: MouseEvent) {\n e.preventDefault()\n e.stopPropagation()\n showAt(e.clientX, e.clientY)\n}\n\nfunction showAt(x: number, y: number) {\n position.value = { x, y }\n visible.value = true\n}\n\nfunction hide() {\n visible.value = false\n}\n\ndefineExpose({ show, showAt, hide })\n</script>\n\n<template>\n <slot :show=\"show\" />\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-100\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"fixed inset-0 z-[200]\"\n @mousedown.self=\"hide\"\n @contextmenu.prevent\n >\n <MContextMenuPanel\n :items=\"items\"\n :x=\"position.x\"\n :y=\"position.y\"\n @close=\"hide\"\n />\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nconst props = defineProps<{ page: number; perPage: number; total: number }>()\nconst emit = defineEmits<{ 'update:page': [number] }>()\n\nconst totalPages = computed(() => Math.max(1, Math.ceil(props.total / props.perPage)))\n\nconst rangeLabel = computed(() => {\n if (props.total === 0) return '0 resultados'\n const from = (props.page - 1) * props.perPage + 1\n const to = Math.min(props.page * props.perPage, props.total)\n return `${from}-${to} de ${props.total}`\n})\n</script>\n\n<template>\n <div class=\"flex flex-wrap items-center justify-between gap-4 text-body-medium text-on-surface-variant\">\n <span>{{ rangeLabel }}</span>\n <div class=\"flex items-center gap-2\">\n <span>Página {{ page }} de {{ totalPages }}</span>\n <MIconButton\n icon=\"chevron_left\"\n label=\"Página anterior\"\n :disabled=\"page <= 1\"\n @click=\"emit('update:page', page - 1)\"\n />\n <MIconButton\n icon=\"chevron_right\"\n label=\"Página siguiente\"\n :disabled=\"page >= totalPages\"\n @click=\"emit('update:page', page + 1)\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nconst props = defineProps<{ page: number; perPage: number; total: number }>()\nconst emit = defineEmits<{ 'update:page': [number] }>()\n\nconst totalPages = computed(() => Math.max(1, Math.ceil(props.total / props.perPage)))\n\nconst rangeLabel = computed(() => {\n if (props.total === 0) return '0 resultados'\n const from = (props.page - 1) * props.perPage + 1\n const to = Math.min(props.page * props.perPage, props.total)\n return `${from}-${to} de ${props.total}`\n})\n</script>\n\n<template>\n <div class=\"flex flex-wrap items-center justify-between gap-4 text-body-medium text-on-surface-variant\">\n <span>{{ rangeLabel }}</span>\n <div class=\"flex items-center gap-2\">\n <span>Página {{ page }} de {{ totalPages }}</span>\n <MIconButton\n icon=\"chevron_left\"\n label=\"Página anterior\"\n :disabled=\"page <= 1\"\n @click=\"emit('update:page', page - 1)\"\n />\n <MIconButton\n icon=\"chevron_right\"\n label=\"Página siguiente\"\n :disabled=\"page >= totalPages\"\n @click=\"emit('update:page', page + 1)\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useSlots, watch } from 'vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MPagination from './MPagination.vue'\nimport MChip from './MChip.vue'\n\nexport interface DataTableColumn {\n key: string\n label: string\n sortable?: boolean\n filterable?: boolean\n resizable?: boolean\n width?: string\n minWidth?: string\n align?: 'left' | 'center' | 'right'\n pinned?: 'left' | 'right'\n hidden?: boolean\n}\n\nexport interface DataTableGroup {\n key: string\n label: string\n}\n\nconst SKEL = [65, 80, 50, 75, 90, 55, 70, 85, 60, 78]\n\nconst props = withDefaults(defineProps<{\n columns: DataTableColumn[]\n rows: Record<string, any>[]\n loading?: boolean\n emptyText?: string\n rowKey?: string\n selectable?: boolean\n modelValue?: Record<string, any>[]\n perPage?: number\n searchable?: boolean\n expandable?: boolean\n striped?: boolean\n dense?: boolean\n stickyHeader?: boolean\n groupBy?: string\n columnToggle?: boolean\n exportable?: boolean\n}>(), {\n loading: false,\n emptyText: 'Sin resultados',\n rowKey: 'id',\n selectable: false,\n modelValue: () => [],\n perPage: 10,\n searchable: true,\n expandable: false,\n striped: false,\n dense: false,\n stickyHeader: false,\n columnToggle: false,\n exportable: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [Record<string, any>[]]\n rowClick: [Record<string, any>]\n}>()\n\nconst slots = useSlots()\nconst hasActions = computed(() => !!slots['row-actions'])\nconst hasExpand = computed(() => props.expandable && !!slots['row-expand'])\n\nconst search = ref('')\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\nconst internalPage = ref(1)\nconst expanded = ref<Set<any>>(new Set())\nconst hiddenCols = ref<Set<string>>(new Set())\nconst colWidths = ref<Record<string, number>>({})\nconst showColMenu = ref(false)\n\nconst visibleColumns = computed(() =>\n props.columns.filter(c => !c.hidden && !hiddenCols.value.has(c.key))\n)\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) { sortKey.value = key; sortDir.value = 'asc' }\n else if (sortDir.value === 'asc') sortDir.value = 'desc'\n else { sortKey.value = ''; sortDir.value = '' }\n}\n\nconst processedRows = computed(() => {\n let result = props.rows\n if (search.value.trim()) {\n const q = search.value.toLowerCase()\n result = result.filter(row =>\n visibleColumns.value.some(col => {\n const val = row[col.key]\n return val != null && String(val).toLowerCase().includes(q)\n })\n )\n }\n if (sortKey.value && sortDir.value) {\n const key = sortKey.value, dir = sortDir.value\n result = [...result].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, { numeric: true, sensitivity: 'base' })\n return dir === 'asc' ? cmp : -cmp\n })\n }\n return result\n})\n\nconst groupedRows = computed(() => {\n if (!props.groupBy) return null\n const map = new Map<string, Record<string, any>[]>()\n for (const row of processedRows.value) {\n const key = String(row[props.groupBy] ?? 'Sin grupo')\n if (!map.has(key)) map.set(key, [])\n map.get(key)!.push(row)\n }\n return map\n})\n\nconst totalCount = computed(() => processedRows.value.length)\nconst visibleRows = computed(() => {\n const start = (internalPage.value - 1) * props.perPage\n return processedRows.value.slice(start, start + props.perPage)\n})\n\nwatch([search, sortKey, sortDir], () => { internalPage.value = 1 })\n\nconst selected = computed({\n get: () => props.modelValue ?? [],\n set: (val) => emit('update:modelValue', val),\n})\nfunction rowId(row: Record<string, any>) { return row[props.rowKey] }\nfunction isSelected(row: Record<string, any>) { return selected.value.some(r => rowId(r) === rowId(row)) }\nfunction toggleRow(row: Record<string, any>) {\n if (isSelected(row)) selected.value = selected.value.filter(r => rowId(r) !== rowId(row))\n else selected.value = [...selected.value, row]\n}\nconst allOnPageSelected = computed(() => visibleRows.value.length > 0 && visibleRows.value.every(r => isSelected(r)))\nconst someOnPageSelected = computed(() => visibleRows.value.some(r => isSelected(r)) && !allOnPageSelected.value)\nfunction toggleAll() {\n if (allOnPageSelected.value) selected.value = selected.value.filter(r => !visibleRows.value.some(v => rowId(v) === rowId(r)))\n else selected.value = [...selected.value, ...visibleRows.value.filter(r => !isSelected(r))]\n}\n\nfunction toggleExpand(row: Record<string, any>) {\n const id = rowId(row)\n const next = new Set(expanded.value)\n next.has(id) ? next.delete(id) : next.add(id)\n expanded.value = next\n}\nfunction isExpanded(row: Record<string, any>) { return expanded.value.has(rowId(row)) }\n\nconst extraCols = computed(() =>\n (props.selectable ? 1 : 0) + (hasActions.value ? 1 : 0) + (hasExpand.value ? 1 : 0)\n)\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\nfunction skelWidth(ri: number, ci: number) { return `${SKEL[(ri * 3 + ci) % SKEL.length]}%` }\n\nlet resizeCol: string | null = null\nlet resizeStart = 0\nlet resizeInitial = 0\n\nfunction onResizeDown(e: PointerEvent, col: DataTableColumn) {\n e.preventDefault()\n resizeCol = col.key\n resizeStart = e.clientX\n resizeInitial = colWidths.value[col.key] ?? 150\n window.addEventListener('pointermove', onResizeMove)\n window.addEventListener('pointerup', onResizeUp)\n}\nfunction onResizeMove(e: PointerEvent) {\n if (!resizeCol) return\n const w = Math.max(60, resizeInitial + e.clientX - resizeStart)\n colWidths.value = { ...colWidths.value, [resizeCol]: w }\n}\nfunction onResizeUp() {\n resizeCol = null\n window.removeEventListener('pointermove', onResizeMove)\n window.removeEventListener('pointerup', onResizeUp)\n}\n\nfunction exportCSV() {\n const cols = visibleColumns.value\n const header = cols.map(c => c.label).join(',')\n const body = processedRows.value.map(row =>\n cols.map(c => {\n const v = String(row[c.key] ?? '')\n return v.includes(',') || v.includes('\"') ? `\"${v.replace(/\"/g, '\"\"')}\"` : v\n }).join(',')\n ).join('\\n')\n const blob = new Blob([`${header}\\n${body}`], { type: 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url; a.download = 'data.csv'; a.click()\n URL.revokeObjectURL(url)\n}\n\nfunction colStyle(col: DataTableColumn) {\n const w = colWidths.value[col.key]\n if (w) return { width: `${w}px`, minWidth: col.minWidth }\n return { width: col.width, minWidth: col.minWidth }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n\n <!-- Toolbar -->\n <div\n v-if=\"searchable || columnToggle || exportable || $slots.toolbar\"\n class=\"flex flex-wrap items-center gap-3 border-b border-outline-variant bg-surface-container-lowest px-4 py-2.5\"\n >\n <div v-if=\"searchable\" class=\"flex min-w-48 flex-1 items-center gap-2 rounded-full border border-outline-variant bg-surface-container px-3 py-1.5 transition-[border-color,box-shadow] duration-150 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input v-model=\"search\" type=\"text\" placeholder=\"Buscar...\" class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\" />\n <button v-if=\"search\" class=\"text-on-surface-variant transition-colors hover:text-on-surface\" @click=\"search = ''\">\n <MIcon name=\"close\" :size=\"14\" />\n </button>\n </div>\n\n <slot name=\"toolbar\" />\n\n <Transition enter-active-class=\"transition-[opacity,transform] duration-150\" enter-from-class=\"opacity-0 scale-90\" leave-active-class=\"transition-[opacity,transform] duration-100\" leave-to-class=\"opacity-0 scale-90\">\n <span v-if=\"selectable && selected.length > 0\" class=\"rounded-full bg-primary/12 px-3 py-1 text-label-small font-medium text-primary\">\n {{ selected.length }} seleccionado{{ selected.length !== 1 ? 's' : '' }}\n </span>\n </Transition>\n\n <!-- Column toggle -->\n <div v-if=\"columnToggle\" class=\"relative\">\n <MIconButton icon=\"view_column\" label=\"Columnas\" :size=\"36\" @click=\"showColMenu = !showColMenu\" />\n <div v-if=\"showColMenu\" class=\"absolute right-0 top-full z-10 mt-1 min-w-40 rounded-lg bg-surface-container py-2 shadow-elevation-3\">\n <label v-for=\"col in columns\" :key=\"col.key\" class=\"flex cursor-pointer items-center gap-2 px-3 py-1.5 hover:bg-on-surface/4\">\n <MCheckbox\n :model-value=\"!hiddenCols.has(col.key)\"\n @update:model-value=\"hiddenCols.has(col.key) ? hiddenCols.delete(col.key) : hiddenCols.add(col.key)\"\n />\n <span class=\"text-body-small text-on-surface\">{{ col.label }}</span>\n </label>\n </div>\n </div>\n\n <MIconButton v-if=\"exportable\" icon=\"download\" label=\"Exportar CSV\" :size=\"36\" @click=\"exportCSV\" />\n </div>\n\n <!-- Table -->\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead :class=\"stickyHeader ? 'sticky top-0 z-[1]' : ''\">\n <tr class=\"bg-surface-container-high\">\n <th v-if=\"hasExpand\" class=\"w-10 px-2\" :class=\"dense ? 'py-2' : 'py-3'\" />\n <th v-if=\"selectable\" class=\"w-12 px-4\" :class=\"dense ? 'py-2' : 'py-3'\">\n <MCheckbox :model-value=\"allOnPageSelected\" :indeterminate=\"someOnPageSelected\" @update:model-value=\"toggleAll\" />\n </th>\n <th\n v-for=\"col in visibleColumns\"\n :key=\"col.key\"\n :style=\"colStyle(col)\"\n :class=\"[\n 'relative whitespace-nowrap text-label-medium font-medium text-on-surface-variant',\n dense ? 'px-3 py-2' : 'px-4 py-3',\n alignClass(col.align),\n col.sortable ? 'cursor-pointer select-none hover:text-on-surface transition-colors duration-100' : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon v-if=\"sortKey === col.key && sortDir === 'asc'\" name=\"arrow_upward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else-if=\"sortKey === col.key && sortDir === 'desc'\" name=\"arrow_downward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n <!-- Resize handle -->\n <div\n v-if=\"col.resizable\"\n class=\"absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-primary/30\"\n @pointerdown=\"onResizeDown($event, col)\"\n />\n </th>\n <th v-if=\"hasActions\" class=\"w-1 px-4\" :class=\"dense ? 'py-2' : 'py-3'\" />\n </tr>\n </thead>\n\n <tbody>\n <!-- Loading -->\n <template v-if=\"loading\">\n <tr v-for=\"ri in perPage\" :key=\"`sk-${ri}`\" class=\"border-t border-outline-variant\">\n <td v-if=\"hasExpand\" :class=\"dense ? 'px-2 py-2' : 'px-2 py-3'\" />\n <td v-if=\"selectable\" :class=\"dense ? 'px-4 py-2' : 'px-4 py-3.5'\">\n <div class=\"h-4 w-4 animate-pulse rounded bg-on-surface/10\" />\n </td>\n <td v-for=\"(col, ci) in visibleColumns\" :key=\"col.key\" :class=\"dense ? 'px-3 py-2' : 'px-4 py-3.5'\">\n <div class=\"h-4 animate-pulse rounded-full bg-on-surface/10\" :style=\"{ width: skelWidth(ri, ci) }\" />\n </td>\n <td v-if=\"hasActions\" :class=\"dense ? 'px-4 py-2' : 'px-4 py-3.5'\">\n <div class=\"ml-auto h-4 w-16 animate-pulse rounded-full bg-on-surface/10\" />\n </td>\n </tr>\n </template>\n\n <!-- Empty -->\n <template v-else-if=\"visibleRows.length === 0\">\n <tr>\n <td :colspan=\"visibleColumns.length + extraCols\" class=\"border-t border-outline-variant px-4 py-14 text-center\">\n <slot name=\"empty\">\n <MIcon name=\"search_off\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ emptyText }}</p>\n </slot>\n </td>\n </tr>\n </template>\n\n <!-- Data rows -->\n <template v-else>\n <template v-for=\"row in visibleRows\" :key=\"rowId(row)\">\n <tr\n :class=\"[\n 'border-t border-outline-variant transition-colors duration-100',\n 'hover:bg-on-surface/[0.04]',\n selectable && isSelected(row) ? 'bg-primary/[0.06]' : '',\n striped ? 'even:bg-surface-container-lowest' : '',\n selectable ? 'cursor-pointer' : '',\n ]\"\n @click=\"selectable ? toggleRow(row) : emit('rowClick', row)\"\n >\n <td v-if=\"hasExpand\" class=\"px-2\" :class=\"dense ? 'py-1' : 'py-2'\" @click.stop>\n <MIconButton\n icon=\"expand_more\"\n label=\"Expandir\"\n :size=\"28\"\n :class=\"isExpanded(row) ? 'rotate-180' : ''\"\n class=\"transition-transform duration-200\"\n @click=\"toggleExpand(row)\"\n />\n </td>\n <td v-if=\"selectable\" :class=\"dense ? 'px-4 py-1' : 'px-4 py-3'\" @click.stop=\"toggleRow(row)\">\n <MCheckbox :model-value=\"isSelected(row)\" @update:model-value=\"toggleRow(row)\" />\n </td>\n <td\n v-for=\"col in visibleColumns\"\n :key=\"col.key\"\n :class=\"['text-body-medium text-on-surface', alignClass(col.align), dense ? 'px-3 py-1.5' : 'px-4 py-3']\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\" :col=\"col\">\n {{ row[col.key] ?? '—' }}\n </slot>\n </td>\n <td v-if=\"hasActions\" class=\"text-right\" :class=\"dense ? 'px-4 py-1' : 'px-4 py-3'\" @click.stop>\n <slot name=\"row-actions\" :row=\"row\" />\n </td>\n </tr>\n <!-- Expanded content -->\n <tr v-if=\"hasExpand && isExpanded(row)\">\n <td :colspan=\"visibleColumns.length + extraCols\" class=\"border-t border-outline-variant/50 bg-surface-container-lowest px-6 py-4\">\n <slot name=\"row-expand\" :row=\"row\" />\n </td>\n </tr>\n </template>\n </template>\n </tbody>\n </table>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center justify-between gap-4 border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ totalCount }} registro{{ totalCount !== 1 ? 's' : '' }}\n </span>\n <MPagination :page=\"internalPage\" :per-page=\"perPage\" :total=\"totalCount\" @update:page=\"internalPage = $event\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useSlots, watch } from 'vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MPagination from './MPagination.vue'\nimport MChip from './MChip.vue'\n\nexport interface DataTableColumn {\n key: string\n label: string\n sortable?: boolean\n filterable?: boolean\n resizable?: boolean\n width?: string\n minWidth?: string\n align?: 'left' | 'center' | 'right'\n pinned?: 'left' | 'right'\n hidden?: boolean\n}\n\nexport interface DataTableGroup {\n key: string\n label: string\n}\n\nconst SKEL = [65, 80, 50, 75, 90, 55, 70, 85, 60, 78]\n\nconst props = withDefaults(defineProps<{\n columns: DataTableColumn[]\n rows: Record<string, any>[]\n loading?: boolean\n emptyText?: string\n rowKey?: string\n selectable?: boolean\n modelValue?: Record<string, any>[]\n perPage?: number\n searchable?: boolean\n expandable?: boolean\n striped?: boolean\n dense?: boolean\n stickyHeader?: boolean\n groupBy?: string\n columnToggle?: boolean\n exportable?: boolean\n}>(), {\n loading: false,\n emptyText: 'Sin resultados',\n rowKey: 'id',\n selectable: false,\n modelValue: () => [],\n perPage: 10,\n searchable: true,\n expandable: false,\n striped: false,\n dense: false,\n stickyHeader: false,\n columnToggle: false,\n exportable: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [Record<string, any>[]]\n rowClick: [Record<string, any>]\n}>()\n\nconst slots = useSlots()\nconst hasActions = computed(() => !!slots['row-actions'])\nconst hasExpand = computed(() => props.expandable && !!slots['row-expand'])\n\nconst search = ref('')\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\nconst internalPage = ref(1)\nconst expanded = ref<Set<any>>(new Set())\nconst hiddenCols = ref<Set<string>>(new Set())\nconst colWidths = ref<Record<string, number>>({})\nconst showColMenu = ref(false)\n\nconst visibleColumns = computed(() =>\n props.columns.filter(c => !c.hidden && !hiddenCols.value.has(c.key))\n)\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) { sortKey.value = key; sortDir.value = 'asc' }\n else if (sortDir.value === 'asc') sortDir.value = 'desc'\n else { sortKey.value = ''; sortDir.value = '' }\n}\n\nconst processedRows = computed(() => {\n let result = props.rows\n if (search.value.trim()) {\n const q = search.value.toLowerCase()\n result = result.filter(row =>\n visibleColumns.value.some(col => {\n const val = row[col.key]\n return val != null && String(val).toLowerCase().includes(q)\n })\n )\n }\n if (sortKey.value && sortDir.value) {\n const key = sortKey.value, dir = sortDir.value\n result = [...result].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, { numeric: true, sensitivity: 'base' })\n return dir === 'asc' ? cmp : -cmp\n })\n }\n return result\n})\n\nconst groupedRows = computed(() => {\n if (!props.groupBy) return null\n const map = new Map<string, Record<string, any>[]>()\n for (const row of processedRows.value) {\n const key = String(row[props.groupBy] ?? 'Sin grupo')\n if (!map.has(key)) map.set(key, [])\n map.get(key)!.push(row)\n }\n return map\n})\n\nconst totalCount = computed(() => processedRows.value.length)\nconst visibleRows = computed(() => {\n const start = (internalPage.value - 1) * props.perPage\n return processedRows.value.slice(start, start + props.perPage)\n})\n\nwatch([search, sortKey, sortDir], () => { internalPage.value = 1 })\n\nconst selected = computed({\n get: () => props.modelValue ?? [],\n set: (val) => emit('update:modelValue', val),\n})\nfunction rowId(row: Record<string, any>) { return row[props.rowKey] }\nfunction isSelected(row: Record<string, any>) { return selected.value.some(r => rowId(r) === rowId(row)) }\nfunction toggleRow(row: Record<string, any>) {\n if (isSelected(row)) selected.value = selected.value.filter(r => rowId(r) !== rowId(row))\n else selected.value = [...selected.value, row]\n}\nconst allOnPageSelected = computed(() => visibleRows.value.length > 0 && visibleRows.value.every(r => isSelected(r)))\nconst someOnPageSelected = computed(() => visibleRows.value.some(r => isSelected(r)) && !allOnPageSelected.value)\nfunction toggleAll() {\n if (allOnPageSelected.value) selected.value = selected.value.filter(r => !visibleRows.value.some(v => rowId(v) === rowId(r)))\n else selected.value = [...selected.value, ...visibleRows.value.filter(r => !isSelected(r))]\n}\n\nfunction toggleExpand(row: Record<string, any>) {\n const id = rowId(row)\n const next = new Set(expanded.value)\n next.has(id) ? next.delete(id) : next.add(id)\n expanded.value = next\n}\nfunction isExpanded(row: Record<string, any>) { return expanded.value.has(rowId(row)) }\n\nconst extraCols = computed(() =>\n (props.selectable ? 1 : 0) + (hasActions.value ? 1 : 0) + (hasExpand.value ? 1 : 0)\n)\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\nfunction skelWidth(ri: number, ci: number) { return `${SKEL[(ri * 3 + ci) % SKEL.length]}%` }\n\nlet resizeCol: string | null = null\nlet resizeStart = 0\nlet resizeInitial = 0\n\nfunction onResizeDown(e: PointerEvent, col: DataTableColumn) {\n e.preventDefault()\n resizeCol = col.key\n resizeStart = e.clientX\n resizeInitial = colWidths.value[col.key] ?? 150\n window.addEventListener('pointermove', onResizeMove)\n window.addEventListener('pointerup', onResizeUp)\n}\nfunction onResizeMove(e: PointerEvent) {\n if (!resizeCol) return\n const w = Math.max(60, resizeInitial + e.clientX - resizeStart)\n colWidths.value = { ...colWidths.value, [resizeCol]: w }\n}\nfunction onResizeUp() {\n resizeCol = null\n window.removeEventListener('pointermove', onResizeMove)\n window.removeEventListener('pointerup', onResizeUp)\n}\n\nfunction exportCSV() {\n const cols = visibleColumns.value\n const header = cols.map(c => c.label).join(',')\n const body = processedRows.value.map(row =>\n cols.map(c => {\n const v = String(row[c.key] ?? '')\n return v.includes(',') || v.includes('\"') ? `\"${v.replace(/\"/g, '\"\"')}\"` : v\n }).join(',')\n ).join('\\n')\n const blob = new Blob([`${header}\\n${body}`], { type: 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url; a.download = 'data.csv'; a.click()\n URL.revokeObjectURL(url)\n}\n\nfunction colStyle(col: DataTableColumn) {\n const w = colWidths.value[col.key]\n if (w) return { width: `${w}px`, minWidth: col.minWidth }\n return { width: col.width, minWidth: col.minWidth }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n\n <!-- Toolbar -->\n <div\n v-if=\"searchable || columnToggle || exportable || $slots.toolbar\"\n class=\"flex flex-wrap items-center gap-3 border-b border-outline-variant bg-surface-container-lowest px-4 py-2.5\"\n >\n <div v-if=\"searchable\" class=\"flex min-w-48 flex-1 items-center gap-2 rounded-full border border-outline-variant bg-surface-container px-3 py-1.5 transition-[border-color,box-shadow] duration-150 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input v-model=\"search\" type=\"text\" placeholder=\"Buscar...\" class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\" />\n <button v-if=\"search\" class=\"text-on-surface-variant transition-colors hover:text-on-surface\" @click=\"search = ''\">\n <MIcon name=\"close\" :size=\"14\" />\n </button>\n </div>\n\n <slot name=\"toolbar\" />\n\n <Transition enter-active-class=\"transition-[opacity,transform] duration-150\" enter-from-class=\"opacity-0 scale-90\" leave-active-class=\"transition-[opacity,transform] duration-100\" leave-to-class=\"opacity-0 scale-90\">\n <span v-if=\"selectable && selected.length > 0\" class=\"rounded-full bg-primary/12 px-3 py-1 text-label-small font-medium text-primary\">\n {{ selected.length }} seleccionado{{ selected.length !== 1 ? 's' : '' }}\n </span>\n </Transition>\n\n <!-- Column toggle -->\n <div v-if=\"columnToggle\" class=\"relative\">\n <MIconButton icon=\"view_column\" label=\"Columnas\" :size=\"36\" @click=\"showColMenu = !showColMenu\" />\n <div v-if=\"showColMenu\" class=\"absolute right-0 top-full z-10 mt-1 min-w-40 rounded-lg bg-surface-container py-2 shadow-elevation-3\">\n <label v-for=\"col in columns\" :key=\"col.key\" class=\"flex cursor-pointer items-center gap-2 px-3 py-1.5 hover:bg-on-surface/4\">\n <MCheckbox\n :model-value=\"!hiddenCols.has(col.key)\"\n @update:model-value=\"hiddenCols.has(col.key) ? hiddenCols.delete(col.key) : hiddenCols.add(col.key)\"\n />\n <span class=\"text-body-small text-on-surface\">{{ col.label }}</span>\n </label>\n </div>\n </div>\n\n <MIconButton v-if=\"exportable\" icon=\"download\" label=\"Exportar CSV\" :size=\"36\" @click=\"exportCSV\" />\n </div>\n\n <!-- Table -->\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead :class=\"stickyHeader ? 'sticky top-0 z-[1]' : ''\">\n <tr class=\"bg-surface-container-high\">\n <th v-if=\"hasExpand\" class=\"w-10 px-2\" :class=\"dense ? 'py-2' : 'py-3'\" />\n <th v-if=\"selectable\" class=\"w-12 px-4\" :class=\"dense ? 'py-2' : 'py-3'\">\n <MCheckbox :model-value=\"allOnPageSelected\" :indeterminate=\"someOnPageSelected\" @update:model-value=\"toggleAll\" />\n </th>\n <th\n v-for=\"col in visibleColumns\"\n :key=\"col.key\"\n :style=\"colStyle(col)\"\n :class=\"[\n 'relative whitespace-nowrap text-label-medium font-medium text-on-surface-variant',\n dense ? 'px-3 py-2' : 'px-4 py-3',\n alignClass(col.align),\n col.sortable ? 'cursor-pointer select-none hover:text-on-surface transition-colors duration-100' : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon v-if=\"sortKey === col.key && sortDir === 'asc'\" name=\"arrow_upward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else-if=\"sortKey === col.key && sortDir === 'desc'\" name=\"arrow_downward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n <!-- Resize handle -->\n <div\n v-if=\"col.resizable\"\n class=\"absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-primary/30\"\n @pointerdown=\"onResizeDown($event, col)\"\n />\n </th>\n <th v-if=\"hasActions\" class=\"w-1 px-4\" :class=\"dense ? 'py-2' : 'py-3'\" />\n </tr>\n </thead>\n\n <tbody>\n <!-- Loading -->\n <template v-if=\"loading\">\n <tr v-for=\"ri in perPage\" :key=\"`sk-${ri}`\" class=\"border-t border-outline-variant\">\n <td v-if=\"hasExpand\" :class=\"dense ? 'px-2 py-2' : 'px-2 py-3'\" />\n <td v-if=\"selectable\" :class=\"dense ? 'px-4 py-2' : 'px-4 py-3.5'\">\n <div class=\"h-4 w-4 animate-pulse rounded bg-on-surface/10\" />\n </td>\n <td v-for=\"(col, ci) in visibleColumns\" :key=\"col.key\" :class=\"dense ? 'px-3 py-2' : 'px-4 py-3.5'\">\n <div class=\"h-4 animate-pulse rounded-full bg-on-surface/10\" :style=\"{ width: skelWidth(ri, ci) }\" />\n </td>\n <td v-if=\"hasActions\" :class=\"dense ? 'px-4 py-2' : 'px-4 py-3.5'\">\n <div class=\"ml-auto h-4 w-16 animate-pulse rounded-full bg-on-surface/10\" />\n </td>\n </tr>\n </template>\n\n <!-- Empty -->\n <template v-else-if=\"visibleRows.length === 0\">\n <tr>\n <td :colspan=\"visibleColumns.length + extraCols\" class=\"border-t border-outline-variant px-4 py-14 text-center\">\n <slot name=\"empty\">\n <MIcon name=\"search_off\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ emptyText }}</p>\n </slot>\n </td>\n </tr>\n </template>\n\n <!-- Data rows -->\n <template v-else>\n <template v-for=\"row in visibleRows\" :key=\"rowId(row)\">\n <tr\n :class=\"[\n 'border-t border-outline-variant transition-colors duration-100',\n 'hover:bg-on-surface/[0.04]',\n selectable && isSelected(row) ? 'bg-primary/[0.06]' : '',\n striped ? 'even:bg-surface-container-lowest' : '',\n selectable ? 'cursor-pointer' : '',\n ]\"\n @click=\"selectable ? toggleRow(row) : emit('rowClick', row)\"\n >\n <td v-if=\"hasExpand\" class=\"px-2\" :class=\"dense ? 'py-1' : 'py-2'\" @click.stop>\n <MIconButton\n icon=\"expand_more\"\n label=\"Expandir\"\n :size=\"28\"\n :class=\"isExpanded(row) ? 'rotate-180' : ''\"\n class=\"transition-transform duration-200\"\n @click=\"toggleExpand(row)\"\n />\n </td>\n <td v-if=\"selectable\" :class=\"dense ? 'px-4 py-1' : 'px-4 py-3'\" @click.stop=\"toggleRow(row)\">\n <MCheckbox :model-value=\"isSelected(row)\" @update:model-value=\"toggleRow(row)\" />\n </td>\n <td\n v-for=\"col in visibleColumns\"\n :key=\"col.key\"\n :class=\"['text-body-medium text-on-surface', alignClass(col.align), dense ? 'px-3 py-1.5' : 'px-4 py-3']\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\" :col=\"col\">\n {{ row[col.key] ?? '—' }}\n </slot>\n </td>\n <td v-if=\"hasActions\" class=\"text-right\" :class=\"dense ? 'px-4 py-1' : 'px-4 py-3'\" @click.stop>\n <slot name=\"row-actions\" :row=\"row\" />\n </td>\n </tr>\n <!-- Expanded content -->\n <tr v-if=\"hasExpand && isExpanded(row)\">\n <td :colspan=\"visibleColumns.length + extraCols\" class=\"border-t border-outline-variant/50 bg-surface-container-lowest px-6 py-4\">\n <slot name=\"row-expand\" :row=\"row\" />\n </td>\n </tr>\n </template>\n </template>\n </tbody>\n </table>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center justify-between gap-4 border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ totalCount }} registro{{ totalCount !== 1 ? 's' : '' }}\n </span>\n <MPagination :page=\"internalPage\" :per-page=\"perPage\" :total=\"totalCount\" @update:page=\"internalPage = $event\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string | null\n label?: string\n placeholder?: string\n min?: string\n max?: string\n disabled?: boolean\n error?: string\n hint?: string\n locale?: string\n fieldBg?: string\n}>(), { locale: 'es-ES' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | null] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst viewDate = ref(props.modelValue ? new Date(props.modelValue + 'T00:00:00') : new Date())\nwatch(() => props.modelValue, (v) => {\n if (v) viewDate.value = new Date(v + 'T00:00:00')\n})\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' })\n return Array.from({ length: 7 }, (_, i) => {\n const d = new Date(2024, 0, i + 1) // Mon=1 Jan 2024\n return f.format(d)\n })\n})()\n\nconst monthLabel = computed(() => {\n const f = new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' })\n return f.format(viewDate.value)\n})\n\nconst calendarDays = computed(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: { date: number; current: boolean; iso: string; disabled: boolean }[] = []\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, disabled: isOutOfRange(iso) })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, disabled: isOutOfRange(iso) })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, disabled: isOutOfRange(iso) })\n }\n return days\n})\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n const yy = dt.getFullYear()\n const mm = String(dt.getMonth() + 1).padStart(2, '0')\n const dd = String(dt.getDate()).padStart(2, '0')\n return `${yy}-${mm}-${dd}`\n}\n\nfunction isOutOfRange(iso: string) {\n if (props.min && iso < props.min) return true\n if (props.max && iso > props.max) return true\n return false\n}\n\nconst isToday = (iso: string) => iso === fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\nfunction prevMonth() {\n const d = new Date(viewDate.value)\n d.setMonth(d.getMonth() - 1)\n viewDate.value = d\n}\nfunction nextMonth() {\n const d = new Date(viewDate.value)\n d.setMonth(d.getMonth() + 1)\n viewDate.value = d\n}\n\nfunction selectDay(day: typeof calendarDays.value[0]) {\n if (day.disabled) return\n emit('update:modelValue', day.iso)\n open.value = false\n}\n\nfunction clear() {\n emit('update:modelValue', null)\n}\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n const d = new Date(props.modelValue + 'T00:00:00')\n return new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short', year: 'numeric' }).format(d)\n})\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 380\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onClickOutside(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onClickOutside), 0)\n } else {\n document.removeEventListener('mousedown', onClickOutside)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onClickOutside)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <!-- Trigger -->\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"calendar_today\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ placeholder || label || 'Seleccionar fecha' }}</span>\n <MIcon\n v-if=\"modelValue\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <!-- Calendar dropdown -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[320px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Header -->\n <div class=\"mb-3 flex items-center justify-between\">\n <MIconButton icon=\"chevron_left\" label=\"Mes anterior\" :size=\"36\" @click=\"prevMonth\" />\n <span class=\"text-title-small font-medium capitalize text-on-surface\">{{ monthLabel }}</span>\n <MIconButton icon=\"chevron_right\" label=\"Mes siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n\n <!-- Weekday headers -->\n <div class=\"mb-1 grid grid-cols-7 gap-0.5 text-center\">\n <span v-for=\"wd in WEEKDAYS\" :key=\"wd\" class=\"py-1 text-label-small font-medium text-on-surface-variant\">\n {{ wd }}\n </span>\n </div>\n\n <!-- Days grid -->\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n class=\"flex h-9 w-full items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"[\n day.disabled\n ? 'cursor-not-allowed text-on-surface/25'\n : day.iso === modelValue\n ? 'bg-primary text-on-primary'\n : isToday(day.iso)\n ? 'border border-primary text-primary cursor-pointer hover:bg-primary/8'\n : day.current\n ? 'cursor-pointer text-on-surface hover:bg-on-surface/8'\n : 'cursor-pointer text-on-surface-variant/50 hover:bg-on-surface/4',\n ]\"\n :disabled=\"day.disabled\"\n @click=\"selectDay(day)\"\n >\n {{ day.date }}\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string | null\n label?: string\n placeholder?: string\n min?: string\n max?: string\n disabled?: boolean\n error?: string\n hint?: string\n locale?: string\n fieldBg?: string\n}>(), { locale: 'es-ES' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | null] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst viewDate = ref(props.modelValue ? new Date(props.modelValue + 'T00:00:00') : new Date())\nwatch(() => props.modelValue, (v) => {\n if (v) viewDate.value = new Date(v + 'T00:00:00')\n})\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' })\n return Array.from({ length: 7 }, (_, i) => {\n const d = new Date(2024, 0, i + 1) // Mon=1 Jan 2024\n return f.format(d)\n })\n})()\n\nconst monthLabel = computed(() => {\n const f = new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' })\n return f.format(viewDate.value)\n})\n\nconst calendarDays = computed(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: { date: number; current: boolean; iso: string; disabled: boolean }[] = []\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, disabled: isOutOfRange(iso) })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, disabled: isOutOfRange(iso) })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, disabled: isOutOfRange(iso) })\n }\n return days\n})\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n const yy = dt.getFullYear()\n const mm = String(dt.getMonth() + 1).padStart(2, '0')\n const dd = String(dt.getDate()).padStart(2, '0')\n return `${yy}-${mm}-${dd}`\n}\n\nfunction isOutOfRange(iso: string) {\n if (props.min && iso < props.min) return true\n if (props.max && iso > props.max) return true\n return false\n}\n\nconst isToday = (iso: string) => iso === fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\nfunction prevMonth() {\n const d = new Date(viewDate.value)\n d.setMonth(d.getMonth() - 1)\n viewDate.value = d\n}\nfunction nextMonth() {\n const d = new Date(viewDate.value)\n d.setMonth(d.getMonth() + 1)\n viewDate.value = d\n}\n\nfunction selectDay(day: typeof calendarDays.value[0]) {\n if (day.disabled) return\n emit('update:modelValue', day.iso)\n open.value = false\n}\n\nfunction clear() {\n emit('update:modelValue', null)\n}\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n const d = new Date(props.modelValue + 'T00:00:00')\n return new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short', year: 'numeric' }).format(d)\n})\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 380\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onClickOutside(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onClickOutside), 0)\n } else {\n document.removeEventListener('mousedown', onClickOutside)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onClickOutside)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <!-- Trigger -->\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"calendar_today\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ placeholder || label || 'Seleccionar fecha' }}</span>\n <MIcon\n v-if=\"modelValue\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <!-- Calendar dropdown -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[320px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Header -->\n <div class=\"mb-3 flex items-center justify-between\">\n <MIconButton icon=\"chevron_left\" label=\"Mes anterior\" :size=\"36\" @click=\"prevMonth\" />\n <span class=\"text-title-small font-medium capitalize text-on-surface\">{{ monthLabel }}</span>\n <MIconButton icon=\"chevron_right\" label=\"Mes siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n\n <!-- Weekday headers -->\n <div class=\"mb-1 grid grid-cols-7 gap-0.5 text-center\">\n <span v-for=\"wd in WEEKDAYS\" :key=\"wd\" class=\"py-1 text-label-small font-medium text-on-surface-variant\">\n {{ wd }}\n </span>\n </div>\n\n <!-- Days grid -->\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n class=\"flex h-9 w-full items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"[\n day.disabled\n ? 'cursor-not-allowed text-on-surface/25'\n : day.iso === modelValue\n ? 'bg-primary text-on-primary'\n : isToday(day.iso)\n ? 'border border-primary text-primary cursor-pointer hover:bg-primary/8'\n : day.current\n ? 'cursor-pointer text-on-surface hover:bg-on-surface/8'\n : 'cursor-pointer text-on-surface-variant/50 hover:bg-on-surface/4',\n ]\"\n :disabled=\"day.disabled\"\n @click=\"selectDay(day)\"\n >\n {{ day.date }}\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nexport interface DateRange {\n start: string | null\n end: string | null\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: DateRange\n label?: string\n min?: string\n max?: string\n disabled?: boolean\n error?: string\n hint?: string\n locale?: string\n fieldBg?: string\n}>(), { locale: 'es-ES' })\n\nconst emit = defineEmits<{ 'update:modelValue': [DateRange] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst picking = ref<'start' | 'end'>('start')\nconst hovered = ref<string | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst viewDate = ref(\n props.modelValue.start ? new Date(props.modelValue.start + 'T00:00:00') : new Date()\n)\nwatch(() => props.modelValue.start, (v) => {\n if (v) viewDate.value = new Date(v + 'T00:00:00')\n})\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' })\n return Array.from({ length: 7 }, (_, i) => f.format(new Date(2024, 0, i + 1)))\n})()\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(viewDate.value)\n)\n\nconst calendarDays = computed(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: { date: number; current: boolean; iso: string; disabled: boolean }[] = []\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, disabled: isOOR(iso) })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, disabled: isOOR(iso) })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, disabled: isOOR(iso) })\n }\n return days\n})\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`\n}\nfunction isOOR(iso: string) {\n if (props.min && iso < props.min) return true\n if (props.max && iso > props.max) return true\n return false\n}\nconst todayIso = fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\nfunction selectDay(day: typeof calendarDays.value[0]) {\n if (day.disabled) return\n if (picking.value === 'start') {\n emit('update:modelValue', { start: day.iso, end: null })\n picking.value = 'end'\n } else {\n const s = props.modelValue.start!\n if (day.iso < s) {\n emit('update:modelValue', { start: day.iso, end: null })\n } else {\n emit('update:modelValue', { start: s, end: day.iso })\n picking.value = 'start'\n open.value = false\n }\n }\n}\n\nfunction isInRange(iso: string) {\n const { start, end } = props.modelValue\n const effectiveEnd = end ?? hovered.value\n if (!start || !effectiveEnd) return false\n const lo = start < effectiveEnd ? start : effectiveEnd\n const hi = start < effectiveEnd ? effectiveEnd : start\n return iso > lo && iso < hi\n}\n\nfunction prevMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() - 1); viewDate.value = d }\nfunction nextMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() + 1); viewDate.value = d }\n\nconst displayValue = computed(() => {\n const f = new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short' })\n const s = props.modelValue.start ? f.format(new Date(props.modelValue.start + 'T00:00:00')) : '—'\n const e = props.modelValue.end ? f.format(new Date(props.modelValue.end + 'T00:00:00')) : '—'\n if (!props.modelValue.start && !props.modelValue.end) return ''\n return `${s} → ${e}`\n})\n\nfunction clear() { emit('update:modelValue', { start: null, end: null }); picking.value = 'start' }\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 400\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n picking.value = props.modelValue.start && !props.modelValue.end ? 'end' : 'start'\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"date_range\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ label || 'Seleccionar rango' }}</span>\n <MIcon\n v-if=\"modelValue.start || modelValue.end\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[320px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <p class=\"mb-2 text-center text-label-medium text-on-surface-variant\">\n {{ picking === 'start' ? 'Selecciona inicio' : 'Selecciona fin' }}\n </p>\n\n <div class=\"mb-3 flex items-center justify-between\">\n <MIconButton icon=\"chevron_left\" label=\"Anterior\" :size=\"36\" @click=\"prevMonth\" />\n <span class=\"text-title-small font-medium capitalize text-on-surface\">{{ monthLabel }}</span>\n <MIconButton icon=\"chevron_right\" label=\"Siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n\n <div class=\"mb-1 grid grid-cols-7 gap-0.5 text-center\">\n <span v-for=\"wd in WEEKDAYS\" :key=\"wd\" class=\"py-1 text-label-small font-medium text-on-surface-variant\">{{ wd }}</span>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n class=\"flex h-9 w-full items-center justify-center text-body-medium transition-colors duration-100\"\n :class=\"[\n day.disabled\n ? 'cursor-not-allowed text-on-surface/25 rounded-full'\n : day.iso === modelValue.start || day.iso === modelValue.end\n ? 'bg-primary text-on-primary rounded-full'\n : isInRange(day.iso)\n ? 'bg-primary/12 text-on-surface cursor-pointer'\n : day.iso === todayIso\n ? 'border border-primary text-primary rounded-full cursor-pointer hover:bg-primary/8'\n : day.current\n ? 'cursor-pointer text-on-surface rounded-full hover:bg-on-surface/8'\n : 'cursor-pointer text-on-surface-variant/50 rounded-full hover:bg-on-surface/4',\n ]\"\n :disabled=\"day.disabled\"\n @mouseenter=\"picking === 'end' && (hovered = day.iso)\"\n @click=\"selectDay(day)\"\n >\n {{ day.date }}\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nexport interface DateRange {\n start: string | null\n end: string | null\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: DateRange\n label?: string\n min?: string\n max?: string\n disabled?: boolean\n error?: string\n hint?: string\n locale?: string\n fieldBg?: string\n}>(), { locale: 'es-ES' })\n\nconst emit = defineEmits<{ 'update:modelValue': [DateRange] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst picking = ref<'start' | 'end'>('start')\nconst hovered = ref<string | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst viewDate = ref(\n props.modelValue.start ? new Date(props.modelValue.start + 'T00:00:00') : new Date()\n)\nwatch(() => props.modelValue.start, (v) => {\n if (v) viewDate.value = new Date(v + 'T00:00:00')\n})\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' })\n return Array.from({ length: 7 }, (_, i) => f.format(new Date(2024, 0, i + 1)))\n})()\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(viewDate.value)\n)\n\nconst calendarDays = computed(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: { date: number; current: boolean; iso: string; disabled: boolean }[] = []\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, disabled: isOOR(iso) })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, disabled: isOOR(iso) })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, disabled: isOOR(iso) })\n }\n return days\n})\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`\n}\nfunction isOOR(iso: string) {\n if (props.min && iso < props.min) return true\n if (props.max && iso > props.max) return true\n return false\n}\nconst todayIso = fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\nfunction selectDay(day: typeof calendarDays.value[0]) {\n if (day.disabled) return\n if (picking.value === 'start') {\n emit('update:modelValue', { start: day.iso, end: null })\n picking.value = 'end'\n } else {\n const s = props.modelValue.start!\n if (day.iso < s) {\n emit('update:modelValue', { start: day.iso, end: null })\n } else {\n emit('update:modelValue', { start: s, end: day.iso })\n picking.value = 'start'\n open.value = false\n }\n }\n}\n\nfunction isInRange(iso: string) {\n const { start, end } = props.modelValue\n const effectiveEnd = end ?? hovered.value\n if (!start || !effectiveEnd) return false\n const lo = start < effectiveEnd ? start : effectiveEnd\n const hi = start < effectiveEnd ? effectiveEnd : start\n return iso > lo && iso < hi\n}\n\nfunction prevMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() - 1); viewDate.value = d }\nfunction nextMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() + 1); viewDate.value = d }\n\nconst displayValue = computed(() => {\n const f = new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short' })\n const s = props.modelValue.start ? f.format(new Date(props.modelValue.start + 'T00:00:00')) : '—'\n const e = props.modelValue.end ? f.format(new Date(props.modelValue.end + 'T00:00:00')) : '—'\n if (!props.modelValue.start && !props.modelValue.end) return ''\n return `${s} → ${e}`\n})\n\nfunction clear() { emit('update:modelValue', { start: null, end: null }); picking.value = 'start' }\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 400\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n picking.value = props.modelValue.start && !props.modelValue.end ? 'end' : 'start'\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"date_range\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ label || 'Seleccionar rango' }}</span>\n <MIcon\n v-if=\"modelValue.start || modelValue.end\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[320px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <p class=\"mb-2 text-center text-label-medium text-on-surface-variant\">\n {{ picking === 'start' ? 'Selecciona inicio' : 'Selecciona fin' }}\n </p>\n\n <div class=\"mb-3 flex items-center justify-between\">\n <MIconButton icon=\"chevron_left\" label=\"Anterior\" :size=\"36\" @click=\"prevMonth\" />\n <span class=\"text-title-small font-medium capitalize text-on-surface\">{{ monthLabel }}</span>\n <MIconButton icon=\"chevron_right\" label=\"Siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n\n <div class=\"mb-1 grid grid-cols-7 gap-0.5 text-center\">\n <span v-for=\"wd in WEEKDAYS\" :key=\"wd\" class=\"py-1 text-label-small font-medium text-on-surface-variant\">{{ wd }}</span>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n class=\"flex h-9 w-full items-center justify-center text-body-medium transition-colors duration-100\"\n :class=\"[\n day.disabled\n ? 'cursor-not-allowed text-on-surface/25 rounded-full'\n : day.iso === modelValue.start || day.iso === modelValue.end\n ? 'bg-primary text-on-primary rounded-full'\n : isInRange(day.iso)\n ? 'bg-primary/12 text-on-surface cursor-pointer'\n : day.iso === todayIso\n ? 'border border-primary text-primary rounded-full cursor-pointer hover:bg-primary/8'\n : day.current\n ? 'cursor-pointer text-on-surface rounded-full hover:bg-on-surface/8'\n : 'cursor-pointer text-on-surface-variant/50 rounded-full hover:bg-on-surface/4',\n ]\"\n :disabled=\"day.disabled\"\n @mouseenter=\"picking === 'end' && (hovered = day.iso)\"\n @click=\"selectDay(day)\"\n >\n {{ day.date }}\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n vertical?: boolean\n label?: string\n inset?: boolean\n}>(), { vertical: false, inset: false })\n</script>\n\n<template>\n <div\n v-if=\"!vertical\"\n class=\"flex items-center gap-3\"\n :class=\"inset && 'ml-16'\"\n role=\"separator\"\n >\n <div class=\"h-px flex-1 bg-outline-variant\" />\n <span v-if=\"label\" class=\"shrink-0 text-label-small text-on-surface-variant\">{{ label }}</span>\n <div v-if=\"label\" class=\"h-px flex-1 bg-outline-variant\" />\n </div>\n\n <div\n v-else\n class=\"w-px self-stretch bg-outline-variant\"\n role=\"separator\"\n />\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n vertical?: boolean\n label?: string\n inset?: boolean\n}>(), { vertical: false, inset: false })\n</script>\n\n<template>\n <div\n v-if=\"!vertical\"\n class=\"flex items-center gap-3\"\n :class=\"inset && 'ml-16'\"\n role=\"separator\"\n >\n <div class=\"h-px flex-1 bg-outline-variant\" />\n <span v-if=\"label\" class=\"shrink-0 text-label-small text-on-surface-variant\">{{ label }}</span>\n <div v-if=\"label\" class=\"h-px flex-1 bg-outline-variant\" />\n </div>\n\n <div\n v-else\n class=\"w-px self-stretch bg-outline-variant\"\n role=\"separator\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface DragDropItem {\n id: string | number\n [key: string]: any\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: DragDropItem[]\n handle?: boolean\n }>(),\n { handle: false },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [DragDropItem[]]\n reorder: [{ from: number; to: number; items: DragDropItem[] }]\n}>()\n\nconst dragIndex = ref<number | null>(null)\nconst overIndex = ref<number | null>(null)\n\nfunction onDragStart(e: DragEvent, index: number) {\n dragIndex.value = index\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(index))\n }\n}\n\nfunction onDragOver(e: DragEvent, index: number) {\n e.preventDefault()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overIndex.value = index\n}\n\nfunction onDragLeave() {\n overIndex.value = null\n}\n\nfunction onDrop(e: DragEvent, toIndex: number) {\n e.preventDefault()\n const fromIndex = dragIndex.value\n if (fromIndex === null || fromIndex === toIndex) {\n reset()\n return\n }\n\n const items = [...props.modelValue]\n const moved = items.splice(fromIndex, 1)[0]!\n items.splice(toIndex, 0, moved)\n\n emit('update:modelValue', items)\n emit('reorder', { from: fromIndex, to: toIndex, items })\n reset()\n}\n\nfunction onDragEnd() {\n reset()\n}\n\nfunction reset() {\n dragIndex.value = null\n overIndex.value = null\n}\n\nfunction getItemClass(index: number) {\n if (dragIndex.value === index) return 'opacity-30'\n if (overIndex.value === index && dragIndex.value !== null) return 'ring-2 ring-primary ring-inset'\n return ''\n}\n</script>\n\n<template>\n <div class=\"flex flex-col\" role=\"listbox\">\n <div\n v-for=\"(item, index) in modelValue\"\n :key=\"item.id\"\n :draggable=\"!handle\"\n class=\"group flex items-center gap-2 rounded-lg px-3 py-2 transition-all\"\n :class=\"[\n getItemClass(index),\n !handle && 'cursor-grab active:cursor-grabbing',\n ]\"\n @dragstart=\"!handle && onDragStart($event, index)\"\n @dragover=\"onDragOver($event, index)\"\n @dragleave=\"onDragLeave\"\n @drop=\"onDrop($event, index)\"\n @dragend=\"onDragEnd\"\n role=\"option\"\n >\n <div\n v-if=\"handle\"\n class=\"flex shrink-0 cursor-grab items-center justify-center rounded p-0.5 text-on-surface-variant/50 transition-colors hover:text-on-surface-variant active:cursor-grabbing\"\n draggable=\"true\"\n @dragstart=\"onDragStart($event, index)\"\n >\n <MIcon name=\"drag_indicator\" :size=\"20\" />\n </div>\n\n <div class=\"min-w-0 flex-1\">\n <slot :item=\"item\" :index=\"index\">\n <span class=\"text-body-medium text-on-surface\">{{ item.id }}</span>\n </slot>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface DragDropItem {\n id: string | number\n [key: string]: any\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: DragDropItem[]\n handle?: boolean\n }>(),\n { handle: false },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [DragDropItem[]]\n reorder: [{ from: number; to: number; items: DragDropItem[] }]\n}>()\n\nconst dragIndex = ref<number | null>(null)\nconst overIndex = ref<number | null>(null)\n\nfunction onDragStart(e: DragEvent, index: number) {\n dragIndex.value = index\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(index))\n }\n}\n\nfunction onDragOver(e: DragEvent, index: number) {\n e.preventDefault()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overIndex.value = index\n}\n\nfunction onDragLeave() {\n overIndex.value = null\n}\n\nfunction onDrop(e: DragEvent, toIndex: number) {\n e.preventDefault()\n const fromIndex = dragIndex.value\n if (fromIndex === null || fromIndex === toIndex) {\n reset()\n return\n }\n\n const items = [...props.modelValue]\n const moved = items.splice(fromIndex, 1)[0]!\n items.splice(toIndex, 0, moved)\n\n emit('update:modelValue', items)\n emit('reorder', { from: fromIndex, to: toIndex, items })\n reset()\n}\n\nfunction onDragEnd() {\n reset()\n}\n\nfunction reset() {\n dragIndex.value = null\n overIndex.value = null\n}\n\nfunction getItemClass(index: number) {\n if (dragIndex.value === index) return 'opacity-30'\n if (overIndex.value === index && dragIndex.value !== null) return 'ring-2 ring-primary ring-inset'\n return ''\n}\n</script>\n\n<template>\n <div class=\"flex flex-col\" role=\"listbox\">\n <div\n v-for=\"(item, index) in modelValue\"\n :key=\"item.id\"\n :draggable=\"!handle\"\n class=\"group flex items-center gap-2 rounded-lg px-3 py-2 transition-all\"\n :class=\"[\n getItemClass(index),\n !handle && 'cursor-grab active:cursor-grabbing',\n ]\"\n @dragstart=\"!handle && onDragStart($event, index)\"\n @dragover=\"onDragOver($event, index)\"\n @dragleave=\"onDragLeave\"\n @drop=\"onDrop($event, index)\"\n @dragend=\"onDragEnd\"\n role=\"option\"\n >\n <div\n v-if=\"handle\"\n class=\"flex shrink-0 cursor-grab items-center justify-center rounded p-0.5 text-on-surface-variant/50 transition-colors hover:text-on-surface-variant active:cursor-grabbing\"\n draggable=\"true\"\n @dragstart=\"onDragStart($event, index)\"\n >\n <MIcon name=\"drag_indicator\" :size=\"20\" />\n </div>\n\n <div class=\"min-w-0 flex-1\">\n <slot :item=\"item\" :index=\"index\">\n <span class=\"text-body-medium text-on-surface\">{{ item.id }}</span>\n </slot>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n icon?: string\n title: string\n description?: string\n compact?: boolean\n}>(), { icon: 'inbox' })\n</script>\n\n<template>\n <div\n class=\"flex flex-col items-center justify-center text-center\"\n :class=\"compact ? 'gap-2 py-6' : 'gap-3 py-14'\"\n >\n <div\n class=\"flex items-center justify-center rounded-full bg-surface-container-high text-on-surface-variant\"\n :class=\"compact ? 'h-12 w-12' : 'h-16 w-16'\"\n >\n <MIcon :name=\"icon\" :size=\"compact ? 24 : 32\" />\n </div>\n <h3\n class=\"font-medium text-on-surface\"\n :class=\"compact ? 'text-title-small' : 'text-title-medium'\"\n >\n {{ title }}\n </h3>\n <p\n v-if=\"description\"\n class=\"max-w-sm text-on-surface-variant\"\n :class=\"compact ? 'text-body-small' : 'text-body-medium'\"\n >\n {{ description }}\n </p>\n <div v-if=\"$slots.actions\" :class=\"compact ? 'mt-1' : 'mt-2'\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n icon?: string\n title: string\n description?: string\n compact?: boolean\n}>(), { icon: 'inbox' })\n</script>\n\n<template>\n <div\n class=\"flex flex-col items-center justify-center text-center\"\n :class=\"compact ? 'gap-2 py-6' : 'gap-3 py-14'\"\n >\n <div\n class=\"flex items-center justify-center rounded-full bg-surface-container-high text-on-surface-variant\"\n :class=\"compact ? 'h-12 w-12' : 'h-16 w-16'\"\n >\n <MIcon :name=\"icon\" :size=\"compact ? 24 : 32\" />\n </div>\n <h3\n class=\"font-medium text-on-surface\"\n :class=\"compact ? 'text-title-small' : 'text-title-medium'\"\n >\n {{ title }}\n </h3>\n <p\n v-if=\"description\"\n class=\"max-w-sm text-on-surface-variant\"\n :class=\"compact ? 'text-body-small' : 'text-body-medium'\"\n >\n {{ description }}\n </p>\n <div v-if=\"$slots.actions\" :class=\"compact ? 'mt-1' : 'mt-2'\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n title: string\n subtitle?: string\n icon?: string\n modelValue?: boolean\n disabled?: boolean\n variant?: 'outlined' | 'filled' | 'elevated'\n}>(), { disabled: false, variant: 'outlined' })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\nconst internal = ref(false)\nconst isOpen = computed(() =>\n props.modelValue !== undefined ? props.modelValue : internal.value,\n)\n\nfunction toggle() {\n if (props.disabled) return\n const next = !isOpen.value\n if (props.modelValue !== undefined) emit('update:modelValue', next)\n else internal.value = next\n}\n\nconst wrapperClass = computed(() => {\n if (props.variant === 'filled') return 'bg-surface-container-low rounded-md'\n if (props.variant === 'elevated') return 'bg-surface-container-low rounded-md shadow-elevation-1'\n return 'rounded-md border border-outline-variant'\n})\n</script>\n\n<template>\n <div :class=\"wrapperClass\" class=\"overflow-hidden\">\n <!-- Header / trigger -->\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-4 px-5 py-4 text-left transition-colors duration-150 focus-visible:outline-none\"\n :class=\"[\n disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer hover:bg-on-surface/4',\n isOpen ? 'bg-on-surface/4' : '',\n ]\"\n :aria-expanded=\"isOpen\"\n :disabled=\"disabled\"\n @click=\"toggle\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"22\" class=\"shrink-0 text-on-surface-variant\" />\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-body-large font-medium text-on-surface\">{{ title }}</p>\n <p v-if=\"subtitle\" class=\"text-body-small text-on-surface-variant\">{{ subtitle }}</p>\n </div>\n <MIcon\n name=\"expand_more\"\n :size=\"22\"\n class=\"shrink-0 text-on-surface-variant transition-transform duration-200\"\n :class=\"isOpen ? 'rotate-180' : ''\"\n />\n </button>\n\n <!-- Content with height animation -->\n <Transition name=\"expand\">\n <div v-if=\"isOpen\" class=\"expand-grid\">\n <div class=\"expand-body border-t border-outline-variant/60 px-5 py-4\">\n <slot />\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<style scoped>\n/*\n grid-template-rows: 0fr → 1fr expands to the exact content height,\n so the animation is always proportional — no max-height overshoot.\n*/\n.expand-grid {\n display: grid;\n grid-template-rows: 1fr;\n}\n.expand-body {\n min-height: 0; /* required for 0fr to actually collapse */\n overflow: hidden;\n}\n\n.expand-enter-active {\n transition: grid-template-rows 280ms cubic-bezier(0.2, 0, 0, 1);\n}\n.expand-enter-active > .expand-body {\n transition: opacity 220ms ease;\n}\n.expand-enter-from {\n grid-template-rows: 0fr;\n}\n.expand-enter-from > .expand-body {\n opacity: 0;\n}\n\n.expand-leave-active {\n transition: grid-template-rows 220ms cubic-bezier(0.4, 0, 1, 1);\n}\n.expand-leave-active > .expand-body {\n transition: opacity 150ms ease;\n}\n.expand-leave-to {\n grid-template-rows: 0fr;\n}\n.expand-leave-to > .expand-body {\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n title: string\n subtitle?: string\n icon?: string\n modelValue?: boolean\n disabled?: boolean\n variant?: 'outlined' | 'filled' | 'elevated'\n}>(), { disabled: false, variant: 'outlined' })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\nconst internal = ref(false)\nconst isOpen = computed(() =>\n props.modelValue !== undefined ? props.modelValue : internal.value,\n)\n\nfunction toggle() {\n if (props.disabled) return\n const next = !isOpen.value\n if (props.modelValue !== undefined) emit('update:modelValue', next)\n else internal.value = next\n}\n\nconst wrapperClass = computed(() => {\n if (props.variant === 'filled') return 'bg-surface-container-low rounded-md'\n if (props.variant === 'elevated') return 'bg-surface-container-low rounded-md shadow-elevation-1'\n return 'rounded-md border border-outline-variant'\n})\n</script>\n\n<template>\n <div :class=\"wrapperClass\" class=\"overflow-hidden\">\n <!-- Header / trigger -->\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-4 px-5 py-4 text-left transition-colors duration-150 focus-visible:outline-none\"\n :class=\"[\n disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer hover:bg-on-surface/4',\n isOpen ? 'bg-on-surface/4' : '',\n ]\"\n :aria-expanded=\"isOpen\"\n :disabled=\"disabled\"\n @click=\"toggle\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"22\" class=\"shrink-0 text-on-surface-variant\" />\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-body-large font-medium text-on-surface\">{{ title }}</p>\n <p v-if=\"subtitle\" class=\"text-body-small text-on-surface-variant\">{{ subtitle }}</p>\n </div>\n <MIcon\n name=\"expand_more\"\n :size=\"22\"\n class=\"shrink-0 text-on-surface-variant transition-transform duration-200\"\n :class=\"isOpen ? 'rotate-180' : ''\"\n />\n </button>\n\n <!-- Content with height animation -->\n <Transition name=\"expand\">\n <div v-if=\"isOpen\" class=\"expand-grid\">\n <div class=\"expand-body border-t border-outline-variant/60 px-5 py-4\">\n <slot />\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<style scoped>\n/*\n grid-template-rows: 0fr → 1fr expands to the exact content height,\n so the animation is always proportional — no max-height overshoot.\n*/\n.expand-grid {\n display: grid;\n grid-template-rows: 1fr;\n}\n.expand-body {\n min-height: 0; /* required for 0fr to actually collapse */\n overflow: hidden;\n}\n\n.expand-enter-active {\n transition: grid-template-rows 280ms cubic-bezier(0.2, 0, 0, 1);\n}\n.expand-enter-active > .expand-body {\n transition: opacity 220ms ease;\n}\n.expand-enter-from {\n grid-template-rows: 0fr;\n}\n.expand-enter-from > .expand-body {\n opacity: 0;\n}\n\n.expand-leave-active {\n transition: grid-template-rows 220ms cubic-bezier(0.4, 0, 1, 1);\n}\n.expand-leave-active > .expand-body {\n transition: opacity 150ms ease;\n}\n.expand-leave-to {\n grid-template-rows: 0fr;\n}\n.expand-leave-to > .expand-body {\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface SpeedDialItem {\n icon: string\n label?: string\n onClick?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n icon: string\n label?: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'surface'\n size?: 'small' | 'regular' | 'large'\n disabled?: boolean\n items?: SpeedDialItem[]\n direction?: 'up' | 'down' | 'left' | 'right' | 'radial'\n }>(),\n {\n color: 'primary',\n size: 'regular',\n disabled: false,\n direction: 'up',\n },\n)\n\nconst emit = defineEmits<{ click: [MouseEvent] }>()\n\nconst open = ref(false)\nconst fabEl = ref<HTMLElement>()\n\nconst hasItems = computed(() => !!props.items?.length)\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container',\n secondary: 'bg-secondary-container text-on-secondary-container',\n tertiary: 'bg-tertiary-container text-on-tertiary-container',\n surface: 'bg-surface-container-high text-primary',\n}\n\nconst fabSizeClasses = computed(() => {\n if (props.label) return 'h-14 rounded-2xl px-4 gap-3'\n switch (props.size) {\n case 'small': return 'h-10 w-10 rounded-lg'\n case 'large': return 'h-24 w-24 rounded-[28px]'\n default: return 'h-14 w-14 rounded-2xl'\n }\n})\n\nconst fabIconSize = computed(() => {\n if (props.label) return 24\n switch (props.size) {\n case 'small': return 20\n case 'large': return 36\n default: return 24\n }\n})\n\nconst fabPx = computed(() => {\n if (props.label) return 56\n switch (props.size) {\n case 'small': return 40\n case 'large': return 96\n default: return 56\n }\n})\n\nconst ITEM_PX = 40\nconst ITEM_GAP = 8\n\nfunction getRect(): DOMRect | null {\n return fabEl.value?.getBoundingClientRect() ?? null\n}\n\nfunction itemStyle(index: number): Record<string, string> {\n const rect = getRect()\n if (!rect) return { position: 'fixed', opacity: '0', pointerEvents: 'none' }\n\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n const count = props.items?.length ?? 0\n\n const delay = open.value\n ? `${index * 35}ms`\n : `${(count - 1 - index) * 35}ms`\n const transition = `transform 220ms cubic-bezier(0.2,0,0,1) ${delay}, opacity 180ms ease ${delay}`\n\n if (props.direction === 'radial') {\n const angle = (2 * Math.PI * index) / count - Math.PI / 2\n const r = 80\n const dx = (Math.cos(angle) * r).toFixed(1)\n const dy = (Math.sin(angle) * r).toFixed(1)\n return {\n position: 'fixed',\n top: `${cy - ITEM_PX / 2}px`,\n left: `${cx - ITEM_PX / 2}px`,\n transform: open.value ? `translate(${dx}px, ${dy}px) scale(1)` : 'translate(0,0) scale(0)',\n opacity: open.value ? '1' : '0',\n transition,\n pointerEvents: open.value ? 'auto' : 'none',\n zIndex: '1000',\n }\n }\n\n const step = ITEM_PX + ITEM_GAP\n const offset = fabPx.value / 2 + ITEM_GAP + ITEM_PX / 2 + index * step\n\n const posMap: Record<string, { top: string; left: string }> = {\n up: { top: `${cy - offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },\n down: { top: `${cy + offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },\n left: { top: `${cy - ITEM_PX / 2}px`, left: `${cx - offset - ITEM_PX / 2}px` },\n right: { top: `${cy - ITEM_PX / 2}px`, left: `${cx + offset - ITEM_PX / 2}px` },\n }\n\n const translateFrom: Record<string, string> = {\n up: 'translateY(12px) scale(0.75)',\n down: 'translateY(-12px) scale(0.75)',\n left: 'translateX(12px) scale(0.75)',\n right: 'translateX(-12px) scale(0.75)',\n }\n\n const pos = posMap[props.direction] ?? posMap.up\n\n return {\n position: 'fixed',\n ...pos,\n transform: open.value ? 'translate(0,0) scale(1)' : (translateFrom[props.direction] ?? 'scale(0.75)'),\n opacity: open.value ? '1' : '0',\n transition,\n pointerEvents: open.value ? 'auto' : 'none',\n zIndex: '1000',\n }\n}\n\nconst showLabel = computed(() => props.direction === 'up' || props.direction === 'down')\n\n// Force re-render to recalculate positions on scroll\nconst scrollTick = ref(0)\nfunction onScroll() {\n if (open.value) scrollTick.value++\n}\n\nfunction createRipple(event: PointerEvent | MouseEvent, target?: HTMLElement) {\n const button = (target ?? event.currentTarget) as HTMLElement\n const rect = button.getBoundingClientRect()\n const d = Math.max(rect.width, rect.height) * 2\n const el = document.createElement('span')\n el.className = 'm3-ripple'\n el.style.cssText = `width:${d}px;height:${d}px;top:${event.clientY - rect.top - d / 2}px;left:${event.clientX - rect.left - d / 2}px`\n button.appendChild(el)\n el.addEventListener('animationend', () => el.remove(), { once: true })\n}\n\nfunction handleFabClick(e: PointerEvent) {\n if (hasItems.value) {\n open.value = !open.value\n } else {\n emit('click', e)\n }\n}\n\nfunction handleItemClick(e: PointerEvent, item: SpeedDialItem, buttonEl: HTMLElement) {\n createRipple(e, buttonEl)\n open.value = false\n item.onClick?.()\n}\n\nfunction onDocClick(e: MouseEvent) {\n if (!open.value) return\n if (fabEl.value && !fabEl.value.contains(e.target as Node)) {\n open.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', onDocClick, true)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('click', onDocClick, true)\n window.removeEventListener('scroll', onScroll, true)\n})\n</script>\n\n<template>\n <div ref=\"fabEl\" class=\"relative inline-flex items-center justify-center\">\n <button\n type=\"button\"\n class=\"relative inline-flex cursor-pointer items-center justify-center overflow-hidden shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 disabled:cursor-not-allowed disabled:opacity-[0.38] before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]\"\n :class=\"[colorMap[color], fabSizeClasses]\"\n :disabled=\"disabled\"\n @pointerdown=\"(e) => { createRipple(e); handleFabClick(e) }\"\n >\n <MIcon\n :name=\"icon\"\n :size=\"fabIconSize\"\n class=\"transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)]\"\n :class=\"hasItems && open ? 'rotate-45' : ''\"\n />\n <span v-if=\"label\" class=\"text-label-large font-medium\">{{ label }}</span>\n </button>\n </div>\n\n <Teleport to=\"body\">\n <template v-if=\"hasItems\">\n <!-- hidden dep on scrollTick to force style recalc -->\n <span :data-tick=\"scrollTick\" class=\"hidden\" />\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n :style=\"itemStyle(i)\"\n class=\"flex items-center gap-3\"\n :class=\"showLabel ? 'flex-row-reverse' : ''\"\n >\n <span\n v-if=\"item.label && showLabel\"\n class=\"whitespace-nowrap rounded-md bg-surface-container-high px-3 py-1.5 text-label-medium text-on-surface shadow-elevation-1\"\n >\n {{ item.label }}\n </span>\n\n <button\n type=\"button\"\n class=\"relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]\"\n :class=\"colorMap[color]\"\n :style=\"{ width: `${ITEM_PX}px`, height: `${ITEM_PX}px` }\"\n @pointerdown=\"(e) => handleItemClick(e, item, e.currentTarget as HTMLElement)\"\n >\n <MIcon :name=\"item.icon\" :size=\"20\" />\n </button>\n </div>\n </template>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface SpeedDialItem {\n icon: string\n label?: string\n onClick?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n icon: string\n label?: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'surface'\n size?: 'small' | 'regular' | 'large'\n disabled?: boolean\n items?: SpeedDialItem[]\n direction?: 'up' | 'down' | 'left' | 'right' | 'radial'\n }>(),\n {\n color: 'primary',\n size: 'regular',\n disabled: false,\n direction: 'up',\n },\n)\n\nconst emit = defineEmits<{ click: [MouseEvent] }>()\n\nconst open = ref(false)\nconst fabEl = ref<HTMLElement>()\n\nconst hasItems = computed(() => !!props.items?.length)\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container',\n secondary: 'bg-secondary-container text-on-secondary-container',\n tertiary: 'bg-tertiary-container text-on-tertiary-container',\n surface: 'bg-surface-container-high text-primary',\n}\n\nconst fabSizeClasses = computed(() => {\n if (props.label) return 'h-14 rounded-2xl px-4 gap-3'\n switch (props.size) {\n case 'small': return 'h-10 w-10 rounded-lg'\n case 'large': return 'h-24 w-24 rounded-[28px]'\n default: return 'h-14 w-14 rounded-2xl'\n }\n})\n\nconst fabIconSize = computed(() => {\n if (props.label) return 24\n switch (props.size) {\n case 'small': return 20\n case 'large': return 36\n default: return 24\n }\n})\n\nconst fabPx = computed(() => {\n if (props.label) return 56\n switch (props.size) {\n case 'small': return 40\n case 'large': return 96\n default: return 56\n }\n})\n\nconst ITEM_PX = 40\nconst ITEM_GAP = 8\n\nfunction getRect(): DOMRect | null {\n return fabEl.value?.getBoundingClientRect() ?? null\n}\n\nfunction itemStyle(index: number): Record<string, string> {\n const rect = getRect()\n if (!rect) return { position: 'fixed', opacity: '0', pointerEvents: 'none' }\n\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n const count = props.items?.length ?? 0\n\n const delay = open.value\n ? `${index * 35}ms`\n : `${(count - 1 - index) * 35}ms`\n const transition = `transform 220ms cubic-bezier(0.2,0,0,1) ${delay}, opacity 180ms ease ${delay}`\n\n if (props.direction === 'radial') {\n const angle = (2 * Math.PI * index) / count - Math.PI / 2\n const r = 80\n const dx = (Math.cos(angle) * r).toFixed(1)\n const dy = (Math.sin(angle) * r).toFixed(1)\n return {\n position: 'fixed',\n top: `${cy - ITEM_PX / 2}px`,\n left: `${cx - ITEM_PX / 2}px`,\n transform: open.value ? `translate(${dx}px, ${dy}px) scale(1)` : 'translate(0,0) scale(0)',\n opacity: open.value ? '1' : '0',\n transition,\n pointerEvents: open.value ? 'auto' : 'none',\n zIndex: '1000',\n }\n }\n\n const step = ITEM_PX + ITEM_GAP\n const offset = fabPx.value / 2 + ITEM_GAP + ITEM_PX / 2 + index * step\n\n const posMap: Record<string, { top: string; left: string }> = {\n up: { top: `${cy - offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },\n down: { top: `${cy + offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },\n left: { top: `${cy - ITEM_PX / 2}px`, left: `${cx - offset - ITEM_PX / 2}px` },\n right: { top: `${cy - ITEM_PX / 2}px`, left: `${cx + offset - ITEM_PX / 2}px` },\n }\n\n const translateFrom: Record<string, string> = {\n up: 'translateY(12px) scale(0.75)',\n down: 'translateY(-12px) scale(0.75)',\n left: 'translateX(12px) scale(0.75)',\n right: 'translateX(-12px) scale(0.75)',\n }\n\n const pos = posMap[props.direction] ?? posMap.up\n\n return {\n position: 'fixed',\n ...pos,\n transform: open.value ? 'translate(0,0) scale(1)' : (translateFrom[props.direction] ?? 'scale(0.75)'),\n opacity: open.value ? '1' : '0',\n transition,\n pointerEvents: open.value ? 'auto' : 'none',\n zIndex: '1000',\n }\n}\n\nconst showLabel = computed(() => props.direction === 'up' || props.direction === 'down')\n\n// Force re-render to recalculate positions on scroll\nconst scrollTick = ref(0)\nfunction onScroll() {\n if (open.value) scrollTick.value++\n}\n\nfunction createRipple(event: PointerEvent | MouseEvent, target?: HTMLElement) {\n const button = (target ?? event.currentTarget) as HTMLElement\n const rect = button.getBoundingClientRect()\n const d = Math.max(rect.width, rect.height) * 2\n const el = document.createElement('span')\n el.className = 'm3-ripple'\n el.style.cssText = `width:${d}px;height:${d}px;top:${event.clientY - rect.top - d / 2}px;left:${event.clientX - rect.left - d / 2}px`\n button.appendChild(el)\n el.addEventListener('animationend', () => el.remove(), { once: true })\n}\n\nfunction handleFabClick(e: PointerEvent) {\n if (hasItems.value) {\n open.value = !open.value\n } else {\n emit('click', e)\n }\n}\n\nfunction handleItemClick(e: PointerEvent, item: SpeedDialItem, buttonEl: HTMLElement) {\n createRipple(e, buttonEl)\n open.value = false\n item.onClick?.()\n}\n\nfunction onDocClick(e: MouseEvent) {\n if (!open.value) return\n if (fabEl.value && !fabEl.value.contains(e.target as Node)) {\n open.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', onDocClick, true)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('click', onDocClick, true)\n window.removeEventListener('scroll', onScroll, true)\n})\n</script>\n\n<template>\n <div ref=\"fabEl\" class=\"relative inline-flex items-center justify-center\">\n <button\n type=\"button\"\n class=\"relative inline-flex cursor-pointer items-center justify-center overflow-hidden shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 disabled:cursor-not-allowed disabled:opacity-[0.38] before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]\"\n :class=\"[colorMap[color], fabSizeClasses]\"\n :disabled=\"disabled\"\n @pointerdown=\"(e) => { createRipple(e); handleFabClick(e) }\"\n >\n <MIcon\n :name=\"icon\"\n :size=\"fabIconSize\"\n class=\"transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)]\"\n :class=\"hasItems && open ? 'rotate-45' : ''\"\n />\n <span v-if=\"label\" class=\"text-label-large font-medium\">{{ label }}</span>\n </button>\n </div>\n\n <Teleport to=\"body\">\n <template v-if=\"hasItems\">\n <!-- hidden dep on scrollTick to force style recalc -->\n <span :data-tick=\"scrollTick\" class=\"hidden\" />\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n :style=\"itemStyle(i)\"\n class=\"flex items-center gap-3\"\n :class=\"showLabel ? 'flex-row-reverse' : ''\"\n >\n <span\n v-if=\"item.label && showLabel\"\n class=\"whitespace-nowrap rounded-md bg-surface-container-high px-3 py-1.5 text-label-medium text-on-surface shadow-elevation-1\"\n >\n {{ item.label }}\n </span>\n\n <button\n type=\"button\"\n class=\"relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]\"\n :class=\"colorMap[color]\"\n :style=\"{ width: `${ITEM_PX}px`, height: `${ITEM_PX}px` }\"\n @pointerdown=\"(e) => handleItemClick(e, item, e.currentTarget as HTMLElement)\"\n >\n <MIcon :name=\"item.icon\" :size=\"20\" />\n </button>\n </div>\n </template>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MSpinner from './MSpinner.vue'\n\nexport interface UploadFile {\n file: File\n id: string\n progress: number\n status: 'pending' | 'uploading' | 'done' | 'error'\n preview?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n accept?: string\n multiple?: boolean\n maxSize?: number\n disabled?: boolean\n }>(),\n { multiple: false, disabled: false },\n)\n\nconst emit = defineEmits<{\n select: [UploadFile[]]\n remove: [UploadFile]\n}>()\n\nconst files = ref<UploadFile[]>([])\nconst dragging = ref(false)\nconst inputRef = ref<HTMLInputElement | null>(null)\n\nconst acceptList = computed(() =>\n props.accept ? props.accept.split(',').map((s) => s.trim()) : null,\n)\n\nfunction isAccepted(file: File) {\n if (!acceptList.value) return true\n return acceptList.value.some((a) => {\n if (a.startsWith('.')) return file.name.toLowerCase().endsWith(a.toLowerCase())\n if (a.endsWith('/*')) return file.type.startsWith(a.replace('/*', '/'))\n return file.type === a\n })\n}\n\nfunction formatSize(bytes: number) {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction processFiles(fileList: FileList | File[]) {\n const arr = Array.from(fileList)\n const valid = arr.filter((f) => {\n if (!isAccepted(f)) return false\n if (props.maxSize && f.size > props.maxSize) return false\n return true\n })\n\n const entries: UploadFile[] = valid.map((f) => {\n const entry: UploadFile = {\n file: f,\n id: crypto.randomUUID(),\n progress: 0,\n status: 'pending',\n }\n if (f.type.startsWith('image/')) {\n entry.preview = URL.createObjectURL(f)\n }\n return entry\n })\n\n if (props.multiple) {\n files.value.push(...entries)\n } else {\n files.value.forEach((f) => f.preview && URL.revokeObjectURL(f.preview))\n files.value = entries.slice(0, 1)\n }\n\n emit('select', entries)\n}\n\nfunction onDrop(e: DragEvent) {\n dragging.value = false\n if (props.disabled || !e.dataTransfer?.files.length) return\n processFiles(e.dataTransfer.files)\n}\n\nfunction onFileInput(e: Event) {\n const input = e.target as HTMLInputElement\n if (input.files?.length) processFiles(input.files)\n input.value = ''\n}\n\nfunction removeFile(entry: UploadFile) {\n if (entry.preview) URL.revokeObjectURL(entry.preview)\n files.value = files.value.filter((f) => f.id !== entry.id)\n emit('remove', entry)\n}\n\nfunction openPicker() {\n if (!props.disabled) inputRef.value?.click()\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-3\">\n <!-- Drop zone -->\n <div\n class=\"relative flex min-h-[160px] cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed p-6 transition-colors duration-150\"\n :class=\"[\n disabled\n ? 'cursor-not-allowed border-outline-variant/50 bg-surface-container/30 opacity-60'\n : dragging\n ? 'border-primary bg-primary-container/20'\n : 'border-outline-variant bg-surface-container-lowest hover:border-primary/60 hover:bg-surface-container',\n ]\"\n @click=\"openPicker\"\n @dragenter.prevent=\"dragging = true\"\n @dragover.prevent=\"dragging = true\"\n @dragleave.prevent=\"dragging = false\"\n @drop.prevent=\"onDrop\"\n >\n <MIcon\n :name=\"dragging ? 'downloading' : 'cloud_upload'\"\n :size=\"40\"\n class=\"text-on-surface-variant\"\n />\n <div class=\"text-center\">\n <p class=\"text-body-large text-on-surface\">\n Arrastra archivos aquí o <span class=\"font-medium text-primary\">selecciona</span>\n </p>\n <p v-if=\"accept || maxSize\" class=\"mt-1 text-body-small text-on-surface-variant\">\n <span v-if=\"accept\">{{ accept }}</span>\n <span v-if=\"accept && maxSize\"> · </span>\n <span v-if=\"maxSize\">Máx. {{ formatSize(maxSize) }}</span>\n </p>\n </div>\n </div>\n\n <input\n ref=\"inputRef\"\n type=\"file\"\n class=\"hidden\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n @change=\"onFileInput\"\n />\n\n <!-- File list -->\n <TransitionGroup\n name=\"m3-file\"\n tag=\"div\"\n class=\"flex flex-col gap-2\"\n >\n <div\n v-for=\"entry in files\"\n :key=\"entry.id\"\n class=\"flex items-center gap-3 rounded-lg bg-surface-container p-3\"\n >\n <!-- Preview / icon -->\n <div class=\"flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-md bg-surface-container-high\">\n <img v-if=\"entry.preview\" :src=\"entry.preview\" class=\"h-full w-full object-cover\" />\n <MIcon v-else name=\"description\" :size=\"24\" class=\"text-on-surface-variant\" />\n </div>\n\n <!-- Info -->\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-body-medium text-on-surface\">{{ entry.file.name }}</p>\n <p class=\"text-body-small text-on-surface-variant\">{{ formatSize(entry.file.size) }}</p>\n <!-- Progress bar -->\n <div\n v-if=\"entry.status === 'uploading'\"\n class=\"mt-1.5 h-1 w-full overflow-hidden rounded-full bg-surface-container-highest\"\n >\n <div\n class=\"h-full rounded-full bg-primary transition-[width] duration-300\"\n :style=\"{ width: `${entry.progress}%` }\"\n />\n </div>\n </div>\n\n <!-- Status -->\n <MSpinner v-if=\"entry.status === 'uploading'\" :size=\"20\" />\n <MIcon v-else-if=\"entry.status === 'done'\" name=\"check_circle\" :size=\"20\" class=\"text-success\" />\n <MIcon v-else-if=\"entry.status === 'error'\" name=\"error\" :size=\"20\" class=\"text-error\" />\n\n <MIconButton icon=\"close\" label=\"Eliminar\" :size=\"32\" @click=\"removeFile(entry)\" />\n </div>\n </TransitionGroup>\n </div>\n</template>\n\n<style scoped>\n.m3-file-enter-active,\n.m3-file-leave-active {\n transition: all 0.2s ease;\n}\n.m3-file-enter-from,\n.m3-file-leave-to {\n opacity: 0;\n transform: translateY(-8px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MSpinner from './MSpinner.vue'\n\nexport interface UploadFile {\n file: File\n id: string\n progress: number\n status: 'pending' | 'uploading' | 'done' | 'error'\n preview?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n accept?: string\n multiple?: boolean\n maxSize?: number\n disabled?: boolean\n }>(),\n { multiple: false, disabled: false },\n)\n\nconst emit = defineEmits<{\n select: [UploadFile[]]\n remove: [UploadFile]\n}>()\n\nconst files = ref<UploadFile[]>([])\nconst dragging = ref(false)\nconst inputRef = ref<HTMLInputElement | null>(null)\n\nconst acceptList = computed(() =>\n props.accept ? props.accept.split(',').map((s) => s.trim()) : null,\n)\n\nfunction isAccepted(file: File) {\n if (!acceptList.value) return true\n return acceptList.value.some((a) => {\n if (a.startsWith('.')) return file.name.toLowerCase().endsWith(a.toLowerCase())\n if (a.endsWith('/*')) return file.type.startsWith(a.replace('/*', '/'))\n return file.type === a\n })\n}\n\nfunction formatSize(bytes: number) {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction processFiles(fileList: FileList | File[]) {\n const arr = Array.from(fileList)\n const valid = arr.filter((f) => {\n if (!isAccepted(f)) return false\n if (props.maxSize && f.size > props.maxSize) return false\n return true\n })\n\n const entries: UploadFile[] = valid.map((f) => {\n const entry: UploadFile = {\n file: f,\n id: crypto.randomUUID(),\n progress: 0,\n status: 'pending',\n }\n if (f.type.startsWith('image/')) {\n entry.preview = URL.createObjectURL(f)\n }\n return entry\n })\n\n if (props.multiple) {\n files.value.push(...entries)\n } else {\n files.value.forEach((f) => f.preview && URL.revokeObjectURL(f.preview))\n files.value = entries.slice(0, 1)\n }\n\n emit('select', entries)\n}\n\nfunction onDrop(e: DragEvent) {\n dragging.value = false\n if (props.disabled || !e.dataTransfer?.files.length) return\n processFiles(e.dataTransfer.files)\n}\n\nfunction onFileInput(e: Event) {\n const input = e.target as HTMLInputElement\n if (input.files?.length) processFiles(input.files)\n input.value = ''\n}\n\nfunction removeFile(entry: UploadFile) {\n if (entry.preview) URL.revokeObjectURL(entry.preview)\n files.value = files.value.filter((f) => f.id !== entry.id)\n emit('remove', entry)\n}\n\nfunction openPicker() {\n if (!props.disabled) inputRef.value?.click()\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-3\">\n <!-- Drop zone -->\n <div\n class=\"relative flex min-h-[160px] cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed p-6 transition-colors duration-150\"\n :class=\"[\n disabled\n ? 'cursor-not-allowed border-outline-variant/50 bg-surface-container/30 opacity-60'\n : dragging\n ? 'border-primary bg-primary-container/20'\n : 'border-outline-variant bg-surface-container-lowest hover:border-primary/60 hover:bg-surface-container',\n ]\"\n @click=\"openPicker\"\n @dragenter.prevent=\"dragging = true\"\n @dragover.prevent=\"dragging = true\"\n @dragleave.prevent=\"dragging = false\"\n @drop.prevent=\"onDrop\"\n >\n <MIcon\n :name=\"dragging ? 'downloading' : 'cloud_upload'\"\n :size=\"40\"\n class=\"text-on-surface-variant\"\n />\n <div class=\"text-center\">\n <p class=\"text-body-large text-on-surface\">\n Arrastra archivos aquí o <span class=\"font-medium text-primary\">selecciona</span>\n </p>\n <p v-if=\"accept || maxSize\" class=\"mt-1 text-body-small text-on-surface-variant\">\n <span v-if=\"accept\">{{ accept }}</span>\n <span v-if=\"accept && maxSize\"> · </span>\n <span v-if=\"maxSize\">Máx. {{ formatSize(maxSize) }}</span>\n </p>\n </div>\n </div>\n\n <input\n ref=\"inputRef\"\n type=\"file\"\n class=\"hidden\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n @change=\"onFileInput\"\n />\n\n <!-- File list -->\n <TransitionGroup\n name=\"m3-file\"\n tag=\"div\"\n class=\"flex flex-col gap-2\"\n >\n <div\n v-for=\"entry in files\"\n :key=\"entry.id\"\n class=\"flex items-center gap-3 rounded-lg bg-surface-container p-3\"\n >\n <!-- Preview / icon -->\n <div class=\"flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-md bg-surface-container-high\">\n <img v-if=\"entry.preview\" :src=\"entry.preview\" class=\"h-full w-full object-cover\" />\n <MIcon v-else name=\"description\" :size=\"24\" class=\"text-on-surface-variant\" />\n </div>\n\n <!-- Info -->\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-body-medium text-on-surface\">{{ entry.file.name }}</p>\n <p class=\"text-body-small text-on-surface-variant\">{{ formatSize(entry.file.size) }}</p>\n <!-- Progress bar -->\n <div\n v-if=\"entry.status === 'uploading'\"\n class=\"mt-1.5 h-1 w-full overflow-hidden rounded-full bg-surface-container-highest\"\n >\n <div\n class=\"h-full rounded-full bg-primary transition-[width] duration-300\"\n :style=\"{ width: `${entry.progress}%` }\"\n />\n </div>\n </div>\n\n <!-- Status -->\n <MSpinner v-if=\"entry.status === 'uploading'\" :size=\"20\" />\n <MIcon v-else-if=\"entry.status === 'done'\" name=\"check_circle\" :size=\"20\" class=\"text-success\" />\n <MIcon v-else-if=\"entry.status === 'error'\" name=\"error\" :size=\"20\" class=\"text-error\" />\n\n <MIconButton icon=\"close\" label=\"Eliminar\" :size=\"32\" @click=\"removeFile(entry)\" />\n </div>\n </TransitionGroup>\n </div>\n</template>\n\n<style scoped>\n.m3-file-enter-active,\n.m3-file-leave-active {\n transition: all 0.2s ease;\n}\n.m3-file-enter-from,\n.m3-file-leave-to {\n opacity: 0;\n transform: translateY(-8px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n cols?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n sm?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n md?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n lg?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n xl?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n alignItems?: 'start' | 'center' | 'end' | 'stretch'\n }>(),\n { cols: 1, gap: 'md', alignItems: 'stretch' },\n)\n\nconst colClasses: Record<number, string> = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-2',\n 3: 'grid-cols-3',\n 4: 'grid-cols-4',\n 5: 'grid-cols-5',\n 6: 'grid-cols-6',\n 12: 'grid-cols-12',\n}\n\nconst smColClasses: Record<number, string> = {\n 1: 'sm:grid-cols-1',\n 2: 'sm:grid-cols-2',\n 3: 'sm:grid-cols-3',\n 4: 'sm:grid-cols-4',\n 5: 'sm:grid-cols-5',\n 6: 'sm:grid-cols-6',\n 12: 'sm:grid-cols-12',\n}\n\nconst mdColClasses: Record<number, string> = {\n 1: 'md:grid-cols-1',\n 2: 'md:grid-cols-2',\n 3: 'md:grid-cols-3',\n 4: 'md:grid-cols-4',\n 5: 'md:grid-cols-5',\n 6: 'md:grid-cols-6',\n 12: 'md:grid-cols-12',\n}\n\nconst lgColClasses: Record<number, string> = {\n 1: 'lg:grid-cols-1',\n 2: 'lg:grid-cols-2',\n 3: 'lg:grid-cols-3',\n 4: 'lg:grid-cols-4',\n 5: 'lg:grid-cols-5',\n 6: 'lg:grid-cols-6',\n 12: 'lg:grid-cols-12',\n}\n\nconst xlColClasses: Record<number, string> = {\n 1: 'xl:grid-cols-1',\n 2: 'xl:grid-cols-2',\n 3: 'xl:grid-cols-3',\n 4: 'xl:grid-cols-4',\n 5: 'xl:grid-cols-5',\n 6: 'xl:grid-cols-6',\n 12: 'xl:grid-cols-12',\n}\n\nconst gapClasses: Record<string, string> = {\n none: 'gap-0',\n xs: 'gap-1',\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n xl: 'gap-8',\n}\n\nconst alignClasses: Record<string, string> = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n}\n\nconst classes = computed(() => [\n 'grid',\n colClasses[props.cols],\n props.sm && smColClasses[props.sm],\n props.md && mdColClasses[props.md],\n props.lg && lgColClasses[props.lg],\n props.xl && xlColClasses[props.xl],\n gapClasses[props.gap],\n alignClasses[props.alignItems],\n])\n</script>\n\n<template>\n <div :class=\"classes\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n cols?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n sm?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n md?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n lg?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n xl?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n alignItems?: 'start' | 'center' | 'end' | 'stretch'\n }>(),\n { cols: 1, gap: 'md', alignItems: 'stretch' },\n)\n\nconst colClasses: Record<number, string> = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-2',\n 3: 'grid-cols-3',\n 4: 'grid-cols-4',\n 5: 'grid-cols-5',\n 6: 'grid-cols-6',\n 12: 'grid-cols-12',\n}\n\nconst smColClasses: Record<number, string> = {\n 1: 'sm:grid-cols-1',\n 2: 'sm:grid-cols-2',\n 3: 'sm:grid-cols-3',\n 4: 'sm:grid-cols-4',\n 5: 'sm:grid-cols-5',\n 6: 'sm:grid-cols-6',\n 12: 'sm:grid-cols-12',\n}\n\nconst mdColClasses: Record<number, string> = {\n 1: 'md:grid-cols-1',\n 2: 'md:grid-cols-2',\n 3: 'md:grid-cols-3',\n 4: 'md:grid-cols-4',\n 5: 'md:grid-cols-5',\n 6: 'md:grid-cols-6',\n 12: 'md:grid-cols-12',\n}\n\nconst lgColClasses: Record<number, string> = {\n 1: 'lg:grid-cols-1',\n 2: 'lg:grid-cols-2',\n 3: 'lg:grid-cols-3',\n 4: 'lg:grid-cols-4',\n 5: 'lg:grid-cols-5',\n 6: 'lg:grid-cols-6',\n 12: 'lg:grid-cols-12',\n}\n\nconst xlColClasses: Record<number, string> = {\n 1: 'xl:grid-cols-1',\n 2: 'xl:grid-cols-2',\n 3: 'xl:grid-cols-3',\n 4: 'xl:grid-cols-4',\n 5: 'xl:grid-cols-5',\n 6: 'xl:grid-cols-6',\n 12: 'xl:grid-cols-12',\n}\n\nconst gapClasses: Record<string, string> = {\n none: 'gap-0',\n xs: 'gap-1',\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n xl: 'gap-8',\n}\n\nconst alignClasses: Record<string, string> = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n}\n\nconst classes = computed(() => [\n 'grid',\n colClasses[props.cols],\n props.sm && smColClasses[props.sm],\n props.md && mdColClasses[props.md],\n props.lg && lgColClasses[props.lg],\n props.xl && xlColClasses[props.xl],\n gapClasses[props.gap],\n alignClasses[props.alignItems],\n])\n</script>\n\n<template>\n <div :class=\"classes\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onMounted, onBeforeUnmount } from 'vue'\n\nexport interface HotkeyBinding {\n keys: string\n label: string\n handler: () => void\n group?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n bindings: HotkeyBinding[]\n showOverlay?: boolean\n }>(),\n { showOverlay: false },\n)\n\nconst isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.userAgent)\n\nfunction formatKey(raw: string): string {\n return raw\n .replace(/mod/gi, isMac ? '⌘' : 'Ctrl')\n .replace(/ctrl/gi, isMac ? '⌃' : 'Ctrl')\n .replace(/alt/gi, isMac ? '⌥' : 'Alt')\n .replace(/shift/gi, isMac ? '⇧' : 'Shift')\n .replace(/meta/gi, '⌘')\n .replace(/enter/gi, '↵')\n .replace(/escape/gi, 'Esc')\n .replace(/backspace/gi, '⌫')\n .replace(/delete/gi, '⌦')\n .replace(/arrowup/gi, '↑')\n .replace(/arrowdown/gi, '↓')\n .replace(/arrowleft/gi, '←')\n .replace(/arrowright/gi, '→')\n}\n\nfunction parseCombo(keys: string) {\n return keys.split('+').map(k => k.trim().toLowerCase())\n}\n\nfunction matchesEvent(combo: string[], e: KeyboardEvent): boolean {\n const modifiers = { ctrl: e.ctrlKey, alt: e.altKey, shift: e.shiftKey, meta: e.metaKey, mod: isMac ? e.metaKey : e.ctrlKey }\n const key = e.key.toLowerCase()\n\n for (const part of combo) {\n if (part in modifiers) {\n if (!modifiers[part as keyof typeof modifiers]) return false\n } else if (key !== part) {\n return false\n }\n }\n\n for (const [mod, active] of Object.entries(modifiers)) {\n if (active && !combo.includes(mod)) return false\n }\n\n return true\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n const tag = (e.target as HTMLElement).tagName\n const editable = tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable\n\n for (const binding of props.bindings) {\n if (binding.disabled) continue\n const combo = parseCombo(binding.keys)\n const hasModifier = combo.some(k => ['ctrl', 'alt', 'shift', 'meta', 'mod'].includes(k))\n if (!hasModifier && editable) continue\n if (matchesEvent(combo, e)) {\n e.preventDefault()\n binding.handler()\n return\n }\n }\n}\n\nonMounted(() => document.addEventListener('keydown', onKeydown))\nonBeforeUnmount(() => document.removeEventListener('keydown', onKeydown))\n\nconst grouped = () => {\n const map = new Map<string, HotkeyBinding[]>()\n for (const b of props.bindings) {\n const g = b.group ?? ''\n if (!map.has(g)) map.set(g, [])\n map.get(g)!.push(b)\n }\n return map\n}\n</script>\n\n<template>\n <div v-if=\"showOverlay\" class=\"flex flex-col gap-4\">\n <template v-for=\"[group, bindings] in grouped()\" :key=\"group\">\n <div>\n <p v-if=\"group\" class=\"mb-2 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ group }}\n </p>\n <div class=\"flex flex-col gap-1\">\n <div\n v-for=\"b in bindings\"\n :key=\"b.keys\"\n class=\"flex items-center justify-between rounded-lg px-3 py-2 transition-colors hover:bg-on-surface/4\"\n :class=\"b.disabled && 'opacity-38'\"\n >\n <span class=\"text-body-medium text-on-surface\">{{ b.label }}</span>\n <div class=\"flex items-center gap-0.5\">\n <kbd\n v-for=\"(k, ki) in b.keys.split('+')\"\n :key=\"ki\"\n class=\"inline-flex min-w-[24px] items-center justify-center rounded bg-surface-container px-1.5 py-0.5 text-center text-label-small font-medium text-on-surface-variant\"\n >\n {{ formatKey(k.trim()) }}\n </kbd>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onMounted, onBeforeUnmount } from 'vue'\n\nexport interface HotkeyBinding {\n keys: string\n label: string\n handler: () => void\n group?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n bindings: HotkeyBinding[]\n showOverlay?: boolean\n }>(),\n { showOverlay: false },\n)\n\nconst isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.userAgent)\n\nfunction formatKey(raw: string): string {\n return raw\n .replace(/mod/gi, isMac ? '⌘' : 'Ctrl')\n .replace(/ctrl/gi, isMac ? '⌃' : 'Ctrl')\n .replace(/alt/gi, isMac ? '⌥' : 'Alt')\n .replace(/shift/gi, isMac ? '⇧' : 'Shift')\n .replace(/meta/gi, '⌘')\n .replace(/enter/gi, '↵')\n .replace(/escape/gi, 'Esc')\n .replace(/backspace/gi, '⌫')\n .replace(/delete/gi, '⌦')\n .replace(/arrowup/gi, '↑')\n .replace(/arrowdown/gi, '↓')\n .replace(/arrowleft/gi, '←')\n .replace(/arrowright/gi, '→')\n}\n\nfunction parseCombo(keys: string) {\n return keys.split('+').map(k => k.trim().toLowerCase())\n}\n\nfunction matchesEvent(combo: string[], e: KeyboardEvent): boolean {\n const modifiers = { ctrl: e.ctrlKey, alt: e.altKey, shift: e.shiftKey, meta: e.metaKey, mod: isMac ? e.metaKey : e.ctrlKey }\n const key = e.key.toLowerCase()\n\n for (const part of combo) {\n if (part in modifiers) {\n if (!modifiers[part as keyof typeof modifiers]) return false\n } else if (key !== part) {\n return false\n }\n }\n\n for (const [mod, active] of Object.entries(modifiers)) {\n if (active && !combo.includes(mod)) return false\n }\n\n return true\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n const tag = (e.target as HTMLElement).tagName\n const editable = tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable\n\n for (const binding of props.bindings) {\n if (binding.disabled) continue\n const combo = parseCombo(binding.keys)\n const hasModifier = combo.some(k => ['ctrl', 'alt', 'shift', 'meta', 'mod'].includes(k))\n if (!hasModifier && editable) continue\n if (matchesEvent(combo, e)) {\n e.preventDefault()\n binding.handler()\n return\n }\n }\n}\n\nonMounted(() => document.addEventListener('keydown', onKeydown))\nonBeforeUnmount(() => document.removeEventListener('keydown', onKeydown))\n\nconst grouped = () => {\n const map = new Map<string, HotkeyBinding[]>()\n for (const b of props.bindings) {\n const g = b.group ?? ''\n if (!map.has(g)) map.set(g, [])\n map.get(g)!.push(b)\n }\n return map\n}\n</script>\n\n<template>\n <div v-if=\"showOverlay\" class=\"flex flex-col gap-4\">\n <template v-for=\"[group, bindings] in grouped()\" :key=\"group\">\n <div>\n <p v-if=\"group\" class=\"mb-2 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ group }}\n </p>\n <div class=\"flex flex-col gap-1\">\n <div\n v-for=\"b in bindings\"\n :key=\"b.keys\"\n class=\"flex items-center justify-between rounded-lg px-3 py-2 transition-colors hover:bg-on-surface/4\"\n :class=\"b.disabled && 'opacity-38'\"\n >\n <span class=\"text-body-medium text-on-surface\">{{ b.label }}</span>\n <div class=\"flex items-center gap-0.5\">\n <kbd\n v-for=\"(k, ki) in b.keys.split('+')\"\n :key=\"ki\"\n class=\"inline-flex min-w-[24px] items-center justify-center rounded bg-surface-container px-1.5 py-0.5 text-center text-label-small font-medium text-on-surface-variant\"\n >\n {{ formatKey(k.trim()) }}\n </kbd>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, onBeforeUnmount, watch } from 'vue'\nimport MSpinner from './MSpinner.vue'\n\nconst props = withDefaults(\n defineProps<{\n loading?: boolean\n disabled?: boolean\n threshold?: number\n loadingText?: string\n endText?: string\n ended?: boolean\n }>(),\n {\n loading: false,\n disabled: false,\n threshold: 100,\n loadingText: 'Cargando...',\n endText: 'No hay más elementos',\n ended: false,\n },\n)\n\nconst emit = defineEmits<{ load: [] }>()\n\nconst sentinelRef = ref<HTMLElement | null>(null)\nlet observer: IntersectionObserver | null = null\n\nfunction createObserver() {\n if (observer) observer.disconnect()\n if (props.disabled || props.ended) return\n\n observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0]\n if (entry?.isIntersecting && !props.loading && !props.ended && !props.disabled) {\n emit('load')\n }\n },\n { rootMargin: `0px 0px ${props.threshold}px 0px` },\n )\n\n if (sentinelRef.value) observer.observe(sentinelRef.value)\n}\n\nonMounted(createObserver)\n\nwatch(() => [props.disabled, props.ended], createObserver)\n\nonBeforeUnmount(() => observer?.disconnect())\n</script>\n\n<template>\n <div>\n <slot />\n\n <div ref=\"sentinelRef\" class=\"flex items-center justify-center py-4\">\n <div v-if=\"loading\" class=\"flex items-center gap-3\">\n <MSpinner :size=\"20\" class=\"text-primary\" />\n <span class=\"text-body-medium text-on-surface-variant\">{{ loadingText }}</span>\n </div>\n <p v-else-if=\"ended\" class=\"text-body-small text-on-surface-variant\">\n {{ endText }}\n </p>\n <slot v-else name=\"idle\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, onBeforeUnmount, watch } from 'vue'\nimport MSpinner from './MSpinner.vue'\n\nconst props = withDefaults(\n defineProps<{\n loading?: boolean\n disabled?: boolean\n threshold?: number\n loadingText?: string\n endText?: string\n ended?: boolean\n }>(),\n {\n loading: false,\n disabled: false,\n threshold: 100,\n loadingText: 'Cargando...',\n endText: 'No hay más elementos',\n ended: false,\n },\n)\n\nconst emit = defineEmits<{ load: [] }>()\n\nconst sentinelRef = ref<HTMLElement | null>(null)\nlet observer: IntersectionObserver | null = null\n\nfunction createObserver() {\n if (observer) observer.disconnect()\n if (props.disabled || props.ended) return\n\n observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0]\n if (entry?.isIntersecting && !props.loading && !props.ended && !props.disabled) {\n emit('load')\n }\n },\n { rootMargin: `0px 0px ${props.threshold}px 0px` },\n )\n\n if (sentinelRef.value) observer.observe(sentinelRef.value)\n}\n\nonMounted(createObserver)\n\nwatch(() => [props.disabled, props.ended], createObserver)\n\nonBeforeUnmount(() => observer?.disconnect())\n</script>\n\n<template>\n <div>\n <slot />\n\n <div ref=\"sentinelRef\" class=\"flex items-center justify-center py-4\">\n <div v-if=\"loading\" class=\"flex items-center gap-3\">\n <MSpinner :size=\"20\" class=\"text-primary\" />\n <span class=\"text-body-medium text-on-surface-variant\">{{ loadingText }}</span>\n </div>\n <p v-else-if=\"ended\" class=\"text-body-small text-on-surface-variant\">\n {{ endText }}\n </p>\n <slot v-else name=\"idle\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n data: unknown\n rootName?: string\n expandDepth?: number\n /** @internal — used by recursive instances */\n _depth?: number\n }>(),\n { rootName: 'root', expandDepth: 2, _depth: 0 },\n)\n\nconst expanded = ref(props._depth < props.expandDepth)\n\nconst dataType = computed(() => {\n if (props.data === null) return 'null'\n if (Array.isArray(props.data)) return 'array'\n return typeof props.data\n})\n\nconst isExpandable = computed(() => dataType.value === 'object' || dataType.value === 'array')\n\nconst entries = computed(() => {\n if (dataType.value === 'array') {\n return (props.data as unknown[]).map((v, i) => ({ key: String(i), value: v }))\n }\n if (dataType.value === 'object' && props.data) {\n return Object.entries(props.data as Record<string, unknown>).map(([k, v]) => ({ key: k, value: v }))\n }\n return []\n})\n\nconst childCount = computed(() => entries.value.length)\n\nconst bracketOpen = computed(() => (dataType.value === 'array' ? '[' : '{'))\nconst bracketClose = computed(() => (dataType.value === 'array' ? ']' : '}'))\n\nfunction valueClass(val: unknown) {\n if (val === null) return 'text-on-surface-variant italic'\n switch (typeof val) {\n case 'string': return 'text-success'\n case 'number': return 'text-primary'\n case 'boolean': return 'text-tertiary'\n default: return 'text-on-surface'\n }\n}\n\nfunction formatValue(val: unknown) {\n if (typeof val === 'string') return `\"${val}\"`\n if (val === null) return 'null'\n if (val === undefined) return 'undefined'\n return String(val)\n}\n</script>\n\n<template>\n <div class=\"font-mono text-body-small leading-relaxed\" :class=\"{ 'rounded-lg border border-outline-variant bg-surface-container-lowest p-3': _depth === 0 }\">\n <!-- Expandable node -->\n <template v-if=\"isExpandable\">\n <button\n type=\"button\"\n class=\"group inline-flex cursor-pointer items-center gap-0.5 rounded px-0.5 hover:bg-on-surface/[0.06]\"\n @click=\"expanded = !expanded\"\n >\n <MIcon\n :name=\"expanded ? 'expand_more' : 'chevron_right'\"\n :size=\"16\"\n class=\"text-on-surface-variant transition-transform duration-100\"\n />\n <span v-if=\"_depth === 0 || rootName\" class=\"text-tertiary\">{{ _depth === 0 ? rootName : '' }}</span>\n <span class=\"text-on-surface-variant\">{{ bracketOpen }}</span>\n <span v-if=\"!expanded\" class=\"text-on-surface-variant/60\">\n {{ childCount }} {{ dataType === 'array' ? 'elementos' : 'campos' }}\n </span>\n <span v-if=\"!expanded\" class=\"text-on-surface-variant\">{{ bracketClose }}</span>\n </button>\n\n <div v-if=\"expanded\" class=\"ml-5 border-l border-outline-variant/40 pl-2\">\n <div v-for=\"entry in entries\" :key=\"entry.key\" class=\"flex items-start\">\n <span class=\"shrink-0 text-primary\">{{ dataType === 'array' ? '' : `\"${entry.key}\"` }}</span>\n <span v-if=\"dataType !== 'array'\" class=\"shrink-0 text-on-surface-variant mr-1\">:</span>\n\n <!-- Recursive child -->\n <MJsonViewer\n v-if=\"entry.value !== null && (typeof entry.value === 'object')\"\n :data=\"entry.value\"\n :root-name=\"entry.key\"\n :expand-depth=\"expandDepth\"\n :_depth=\"_depth + 1\"\n />\n <!-- Primitive value -->\n <span v-else :class=\"valueClass(entry.value)\">{{ formatValue(entry.value) }}</span>\n </div>\n </div>\n <span v-if=\"expanded\" class=\"ml-5 text-on-surface-variant\">{{ bracketClose }}</span>\n </template>\n\n <!-- Primitive root -->\n <template v-else>\n <span :class=\"valueClass(data)\">{{ formatValue(data) }}</span>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n data: unknown\n rootName?: string\n expandDepth?: number\n /** @internal — used by recursive instances */\n _depth?: number\n }>(),\n { rootName: 'root', expandDepth: 2, _depth: 0 },\n)\n\nconst expanded = ref(props._depth < props.expandDepth)\n\nconst dataType = computed(() => {\n if (props.data === null) return 'null'\n if (Array.isArray(props.data)) return 'array'\n return typeof props.data\n})\n\nconst isExpandable = computed(() => dataType.value === 'object' || dataType.value === 'array')\n\nconst entries = computed(() => {\n if (dataType.value === 'array') {\n return (props.data as unknown[]).map((v, i) => ({ key: String(i), value: v }))\n }\n if (dataType.value === 'object' && props.data) {\n return Object.entries(props.data as Record<string, unknown>).map(([k, v]) => ({ key: k, value: v }))\n }\n return []\n})\n\nconst childCount = computed(() => entries.value.length)\n\nconst bracketOpen = computed(() => (dataType.value === 'array' ? '[' : '{'))\nconst bracketClose = computed(() => (dataType.value === 'array' ? ']' : '}'))\n\nfunction valueClass(val: unknown) {\n if (val === null) return 'text-on-surface-variant italic'\n switch (typeof val) {\n case 'string': return 'text-success'\n case 'number': return 'text-primary'\n case 'boolean': return 'text-tertiary'\n default: return 'text-on-surface'\n }\n}\n\nfunction formatValue(val: unknown) {\n if (typeof val === 'string') return `\"${val}\"`\n if (val === null) return 'null'\n if (val === undefined) return 'undefined'\n return String(val)\n}\n</script>\n\n<template>\n <div class=\"font-mono text-body-small leading-relaxed\" :class=\"{ 'rounded-lg border border-outline-variant bg-surface-container-lowest p-3': _depth === 0 }\">\n <!-- Expandable node -->\n <template v-if=\"isExpandable\">\n <button\n type=\"button\"\n class=\"group inline-flex cursor-pointer items-center gap-0.5 rounded px-0.5 hover:bg-on-surface/[0.06]\"\n @click=\"expanded = !expanded\"\n >\n <MIcon\n :name=\"expanded ? 'expand_more' : 'chevron_right'\"\n :size=\"16\"\n class=\"text-on-surface-variant transition-transform duration-100\"\n />\n <span v-if=\"_depth === 0 || rootName\" class=\"text-tertiary\">{{ _depth === 0 ? rootName : '' }}</span>\n <span class=\"text-on-surface-variant\">{{ bracketOpen }}</span>\n <span v-if=\"!expanded\" class=\"text-on-surface-variant/60\">\n {{ childCount }} {{ dataType === 'array' ? 'elementos' : 'campos' }}\n </span>\n <span v-if=\"!expanded\" class=\"text-on-surface-variant\">{{ bracketClose }}</span>\n </button>\n\n <div v-if=\"expanded\" class=\"ml-5 border-l border-outline-variant/40 pl-2\">\n <div v-for=\"entry in entries\" :key=\"entry.key\" class=\"flex items-start\">\n <span class=\"shrink-0 text-primary\">{{ dataType === 'array' ? '' : `\"${entry.key}\"` }}</span>\n <span v-if=\"dataType !== 'array'\" class=\"shrink-0 text-on-surface-variant mr-1\">:</span>\n\n <!-- Recursive child -->\n <MJsonViewer\n v-if=\"entry.value !== null && (typeof entry.value === 'object')\"\n :data=\"entry.value\"\n :root-name=\"entry.key\"\n :expand-depth=\"expandDepth\"\n :_depth=\"_depth + 1\"\n />\n <!-- Primitive value -->\n <span v-else :class=\"valueClass(entry.value)\">{{ formatValue(entry.value) }}</span>\n </div>\n </div>\n <span v-if=\"expanded\" class=\"ml-5 text-on-surface-variant\">{{ bracketClose }}</span>\n </template>\n\n <!-- Primitive root -->\n <template v-else>\n <span :class=\"valueClass(data)\">{{ formatValue(data) }}</span>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface KanbanCard {\n id: string | number\n [key: string]: any\n}\n\nexport interface KanbanColumn {\n id: string | number\n title: string\n cards: KanbanCard[]\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n}\n\nconst props = defineProps<{\n modelValue: KanbanColumn[]\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [KanbanColumn[]]\n cardMove: [{ cardId: string | number; fromColumn: string | number; toColumn: string | number; toIndex: number }]\n cardClick: [{ card: KanbanCard; columnId: string | number }]\n}>()\n\nconst dragCard = ref<{ cardId: string | number; columnId: string | number } | null>(null)\nconst overColumn = ref<string | number | null>(null)\nconst overCardIndex = ref<number | null>(null)\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary',\n secondary: 'bg-secondary',\n tertiary: 'bg-tertiary',\n error: 'bg-error',\n success: 'bg-success',\n}\n\nfunction onCardDragStart(e: DragEvent, card: KanbanCard, columnId: string | number) {\n dragCard.value = { cardId: card.id, columnId }\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(card.id))\n }\n}\n\nfunction onColumnDragOver(e: DragEvent, columnId: string | number) {\n e.preventDefault()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overColumn.value = columnId\n}\n\nfunction onCardDragOver(e: DragEvent, _card: KanbanCard, index: number, columnId: string | number) {\n e.preventDefault()\n e.stopPropagation()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overColumn.value = columnId\n overCardIndex.value = index\n}\n\nfunction onDrop(e: DragEvent, toColumnId: string | number) {\n e.preventDefault()\n if (!dragCard.value) return\n\n const { cardId, columnId: fromColumnId } = dragCard.value\n if (fromColumnId === toColumnId && overCardIndex.value === null) {\n reset()\n return\n }\n\n const columns = props.modelValue.map((col) => ({ ...col, cards: [...col.cards] }))\n const fromCol = columns.find((c) => c.id === fromColumnId)\n const toCol = columns.find((c) => c.id === toColumnId)\n if (!fromCol || !toCol) { reset(); return }\n\n const cardIndex = fromCol.cards.findIndex((c) => c.id === cardId)\n if (cardIndex === -1) { reset(); return }\n\n const removed = fromCol.cards.splice(cardIndex, 1)\n const toIndex = overCardIndex.value ?? toCol.cards.length\n toCol.cards.splice(toIndex, 0, removed[0]!)\n\n emit('update:modelValue', columns)\n emit('cardMove', { cardId, fromColumn: fromColumnId, toColumn: toColumnId, toIndex })\n reset()\n}\n\nfunction reset() {\n dragCard.value = null\n overColumn.value = null\n overCardIndex.value = null\n}\n</script>\n\n<template>\n <div class=\"flex gap-4 overflow-x-auto pb-2\">\n <div\n v-for=\"column in modelValue\"\n :key=\"column.id\"\n class=\"flex w-72 shrink-0 flex-col rounded-xl bg-surface-container-low\"\n :class=\"overColumn === column.id && dragCard ? 'ring-2 ring-primary ring-inset' : ''\"\n @dragover=\"onColumnDragOver($event, column.id)\"\n @dragleave=\"overColumn = null\"\n @drop=\"onDrop($event, column.id)\"\n >\n <!-- Column header -->\n <div class=\"flex items-center gap-2 px-4 py-3\">\n <div v-if=\"column.color\" class=\"h-2.5 w-2.5 rounded-full\" :class=\"colorMap[column.color] ?? 'bg-primary'\" />\n <h3 class=\"flex-1 text-title-small font-medium text-on-surface\">{{ column.title }}</h3>\n <span class=\"rounded-full bg-surface-container-high px-2 py-0.5 text-label-small text-on-surface-variant\">\n {{ column.cards.length }}\n </span>\n </div>\n\n <!-- Cards area -->\n <div class=\"flex min-h-[60px] flex-1 flex-col gap-2 px-3 pb-3\">\n <div\n v-for=\"(card, index) in column.cards\"\n :key=\"card.id\"\n draggable=\"true\"\n class=\"cursor-grab rounded-lg bg-surface p-3 shadow-elevation-1 transition-all duration-150 active:cursor-grabbing\"\n :class=\"[\n dragCard?.cardId === card.id ? 'opacity-30' : 'hover:shadow-elevation-2',\n overCardIndex === index && overColumn === column.id && dragCard ? 'border-t-2 border-primary' : '',\n ]\"\n @dragstart=\"onCardDragStart($event, card, column.id)\"\n @dragover=\"onCardDragOver($event, card, index, column.id)\"\n @dragend=\"reset\"\n @click=\"emit('cardClick', { card, columnId: column.id })\"\n >\n <slot name=\"card\" :card=\"card\" :column=\"column\">\n <p class=\"text-body-medium text-on-surface\">{{ card.id }}</p>\n </slot>\n </div>\n\n <!-- Empty state -->\n <div\n v-if=\"column.cards.length === 0\"\n class=\"flex flex-1 items-center justify-center rounded-lg border border-dashed border-outline-variant/50 p-4\"\n >\n <p class=\"text-body-small text-on-surface-variant/60\">Sin tarjetas</p>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface KanbanCard {\n id: string | number\n [key: string]: any\n}\n\nexport interface KanbanColumn {\n id: string | number\n title: string\n cards: KanbanCard[]\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n}\n\nconst props = defineProps<{\n modelValue: KanbanColumn[]\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [KanbanColumn[]]\n cardMove: [{ cardId: string | number; fromColumn: string | number; toColumn: string | number; toIndex: number }]\n cardClick: [{ card: KanbanCard; columnId: string | number }]\n}>()\n\nconst dragCard = ref<{ cardId: string | number; columnId: string | number } | null>(null)\nconst overColumn = ref<string | number | null>(null)\nconst overCardIndex = ref<number | null>(null)\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary',\n secondary: 'bg-secondary',\n tertiary: 'bg-tertiary',\n error: 'bg-error',\n success: 'bg-success',\n}\n\nfunction onCardDragStart(e: DragEvent, card: KanbanCard, columnId: string | number) {\n dragCard.value = { cardId: card.id, columnId }\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(card.id))\n }\n}\n\nfunction onColumnDragOver(e: DragEvent, columnId: string | number) {\n e.preventDefault()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overColumn.value = columnId\n}\n\nfunction onCardDragOver(e: DragEvent, _card: KanbanCard, index: number, columnId: string | number) {\n e.preventDefault()\n e.stopPropagation()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overColumn.value = columnId\n overCardIndex.value = index\n}\n\nfunction onDrop(e: DragEvent, toColumnId: string | number) {\n e.preventDefault()\n if (!dragCard.value) return\n\n const { cardId, columnId: fromColumnId } = dragCard.value\n if (fromColumnId === toColumnId && overCardIndex.value === null) {\n reset()\n return\n }\n\n const columns = props.modelValue.map((col) => ({ ...col, cards: [...col.cards] }))\n const fromCol = columns.find((c) => c.id === fromColumnId)\n const toCol = columns.find((c) => c.id === toColumnId)\n if (!fromCol || !toCol) { reset(); return }\n\n const cardIndex = fromCol.cards.findIndex((c) => c.id === cardId)\n if (cardIndex === -1) { reset(); return }\n\n const removed = fromCol.cards.splice(cardIndex, 1)\n const toIndex = overCardIndex.value ?? toCol.cards.length\n toCol.cards.splice(toIndex, 0, removed[0]!)\n\n emit('update:modelValue', columns)\n emit('cardMove', { cardId, fromColumn: fromColumnId, toColumn: toColumnId, toIndex })\n reset()\n}\n\nfunction reset() {\n dragCard.value = null\n overColumn.value = null\n overCardIndex.value = null\n}\n</script>\n\n<template>\n <div class=\"flex gap-4 overflow-x-auto pb-2\">\n <div\n v-for=\"column in modelValue\"\n :key=\"column.id\"\n class=\"flex w-72 shrink-0 flex-col rounded-xl bg-surface-container-low\"\n :class=\"overColumn === column.id && dragCard ? 'ring-2 ring-primary ring-inset' : ''\"\n @dragover=\"onColumnDragOver($event, column.id)\"\n @dragleave=\"overColumn = null\"\n @drop=\"onDrop($event, column.id)\"\n >\n <!-- Column header -->\n <div class=\"flex items-center gap-2 px-4 py-3\">\n <div v-if=\"column.color\" class=\"h-2.5 w-2.5 rounded-full\" :class=\"colorMap[column.color] ?? 'bg-primary'\" />\n <h3 class=\"flex-1 text-title-small font-medium text-on-surface\">{{ column.title }}</h3>\n <span class=\"rounded-full bg-surface-container-high px-2 py-0.5 text-label-small text-on-surface-variant\">\n {{ column.cards.length }}\n </span>\n </div>\n\n <!-- Cards area -->\n <div class=\"flex min-h-[60px] flex-1 flex-col gap-2 px-3 pb-3\">\n <div\n v-for=\"(card, index) in column.cards\"\n :key=\"card.id\"\n draggable=\"true\"\n class=\"cursor-grab rounded-lg bg-surface p-3 shadow-elevation-1 transition-all duration-150 active:cursor-grabbing\"\n :class=\"[\n dragCard?.cardId === card.id ? 'opacity-30' : 'hover:shadow-elevation-2',\n overCardIndex === index && overColumn === column.id && dragCard ? 'border-t-2 border-primary' : '',\n ]\"\n @dragstart=\"onCardDragStart($event, card, column.id)\"\n @dragover=\"onCardDragOver($event, card, index, column.id)\"\n @dragend=\"reset\"\n @click=\"emit('cardClick', { card, columnId: column.id })\"\n >\n <slot name=\"card\" :card=\"card\" :column=\"column\">\n <p class=\"text-body-medium text-on-surface\">{{ card.id }}</p>\n </slot>\n </div>\n\n <!-- Empty state -->\n <div\n v-if=\"column.cards.length === 0\"\n class=\"flex flex-1 items-center justify-center rounded-lg border border-dashed border-outline-variant/50 p-4\"\n >\n <p class=\"text-body-small text-on-surface-variant/60\">Sin tarjetas</p>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MSpinner from './MSpinner.vue'\n\nwithDefaults(defineProps<{\n visible: boolean\n text?: string\n fullscreen?: boolean\n opaque?: boolean\n spinnerSize?: number\n}>(), { fullscreen: false, opaque: false, spinnerSize: 40 })\n</script>\n\n<template>\n <Teleport v-if=\"fullscreen\" to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-200\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-150\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"fixed inset-0 z-[300] flex flex-col items-center justify-center gap-4\"\n :class=\"opaque ? 'bg-surface' : 'bg-surface/80 backdrop-blur-sm'\"\n >\n <MSpinner :size=\"spinnerSize\" class=\"text-primary\" />\n <p v-if=\"text\" class=\"text-body-large text-on-surface-variant\">{{ text }}</p>\n <slot />\n </div>\n </Transition>\n </Teleport>\n\n <div v-else class=\"relative\">\n <slot name=\"content\" />\n <Transition\n enter-active-class=\"transition-opacity duration-200\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-150\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 rounded-[inherit]\"\n :class=\"opaque ? 'bg-surface' : 'bg-surface/80 backdrop-blur-sm'\"\n >\n <MSpinner :size=\"spinnerSize\" class=\"text-primary\" />\n <p v-if=\"text\" class=\"text-body-medium text-on-surface-variant\">{{ text }}</p>\n <slot />\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MSpinner from './MSpinner.vue'\n\nwithDefaults(defineProps<{\n visible: boolean\n text?: string\n fullscreen?: boolean\n opaque?: boolean\n spinnerSize?: number\n}>(), { fullscreen: false, opaque: false, spinnerSize: 40 })\n</script>\n\n<template>\n <Teleport v-if=\"fullscreen\" to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-200\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-150\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"fixed inset-0 z-[300] flex flex-col items-center justify-center gap-4\"\n :class=\"opaque ? 'bg-surface' : 'bg-surface/80 backdrop-blur-sm'\"\n >\n <MSpinner :size=\"spinnerSize\" class=\"text-primary\" />\n <p v-if=\"text\" class=\"text-body-large text-on-surface-variant\">{{ text }}</p>\n <slot />\n </div>\n </Transition>\n </Teleport>\n\n <div v-else class=\"relative\">\n <slot name=\"content\" />\n <Transition\n enter-active-class=\"transition-opacity duration-200\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-150\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 rounded-[inherit]\"\n :class=\"opaque ? 'bg-surface' : 'bg-surface/80 backdrop-blur-sm'\"\n >\n <MSpinner :size=\"spinnerSize\" class=\"text-primary\" />\n <p v-if=\"text\" class=\"text-body-medium text-on-surface-variant\">{{ text }}</p>\n <slot />\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onBeforeUnmount, watch, nextTick, useSlots } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n cols?: number\n smCols?: number\n mdCols?: number\n lgCols?: number\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n }>(),\n { cols: 2, gap: 'md' },\n)\n\nconst gapPx: Record<string, number> = {\n none: 0,\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n}\n\nconst containerRef = ref<HTMLElement | null>(null)\nconst activeCols = ref(props.cols)\n\nfunction getActiveCols() {\n const w = window.innerWidth\n if (props.lgCols && w >= 1024) return props.lgCols\n if (props.mdCols && w >= 768) return props.mdCols\n if (props.smCols && w >= 640) return props.smCols\n return props.cols\n}\n\nconst slots = useSlots()\n\nfunction layout() {\n activeCols.value = getActiveCols()\n const container = containerRef.value\n if (!container) return\n\n const gap = gapPx[props.gap] ?? 0\n const cols = activeCols.value\n const children = Array.from(container.children) as HTMLElement[]\n\n const colWidth = (container.clientWidth - gap * (cols - 1)) / cols\n const colHeights = new Array<number>(cols).fill(0)\n\n for (const child of children) {\n const shortest = colHeights.indexOf(Math.min(...colHeights))\n const x = shortest * (colWidth + gap)\n const y = colHeights[shortest] ?? 0\n\n child.style.position = 'absolute'\n child.style.left = `${x}px`\n child.style.top = `${y}px`\n child.style.width = `${colWidth}px`\n\n colHeights[shortest] = (colHeights[shortest] ?? 0) + child.offsetHeight + gap\n }\n\n container.style.height = `${Math.max(...colHeights) - gap}px`\n}\n\nlet resizeObserver: ResizeObserver | null = null\n\nonMounted(() => {\n nextTick(layout)\n resizeObserver = new ResizeObserver(layout)\n if (containerRef.value) resizeObserver.observe(containerRef.value)\n window.addEventListener('resize', layout)\n})\n\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n window.removeEventListener('resize', layout)\n})\n\nwatch(() => [props.cols, props.smCols, props.mdCols, props.lgCols, props.gap], () => nextTick(layout))\nwatch(() => slots.default?.(), () => nextTick(layout), { flush: 'post' })\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"relative w-full\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onBeforeUnmount, watch, nextTick, useSlots } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n cols?: number\n smCols?: number\n mdCols?: number\n lgCols?: number\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n }>(),\n { cols: 2, gap: 'md' },\n)\n\nconst gapPx: Record<string, number> = {\n none: 0,\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n}\n\nconst containerRef = ref<HTMLElement | null>(null)\nconst activeCols = ref(props.cols)\n\nfunction getActiveCols() {\n const w = window.innerWidth\n if (props.lgCols && w >= 1024) return props.lgCols\n if (props.mdCols && w >= 768) return props.mdCols\n if (props.smCols && w >= 640) return props.smCols\n return props.cols\n}\n\nconst slots = useSlots()\n\nfunction layout() {\n activeCols.value = getActiveCols()\n const container = containerRef.value\n if (!container) return\n\n const gap = gapPx[props.gap] ?? 0\n const cols = activeCols.value\n const children = Array.from(container.children) as HTMLElement[]\n\n const colWidth = (container.clientWidth - gap * (cols - 1)) / cols\n const colHeights = new Array<number>(cols).fill(0)\n\n for (const child of children) {\n const shortest = colHeights.indexOf(Math.min(...colHeights))\n const x = shortest * (colWidth + gap)\n const y = colHeights[shortest] ?? 0\n\n child.style.position = 'absolute'\n child.style.left = `${x}px`\n child.style.top = `${y}px`\n child.style.width = `${colWidth}px`\n\n colHeights[shortest] = (colHeights[shortest] ?? 0) + child.offsetHeight + gap\n }\n\n container.style.height = `${Math.max(...colHeights) - gap}px`\n}\n\nlet resizeObserver: ResizeObserver | null = null\n\nonMounted(() => {\n nextTick(layout)\n resizeObserver = new ResizeObserver(layout)\n if (containerRef.value) resizeObserver.observe(containerRef.value)\n window.addEventListener('resize', layout)\n})\n\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n window.removeEventListener('resize', layout)\n})\n\nwatch(() => [props.cols, props.smCols, props.mdCols, props.lgCols, props.gap], () => nextTick(layout))\nwatch(() => slots.default?.(), () => nextTick(layout), { flush: 'post' })\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"relative w-full\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n /** Which edge of the trigger the dropdown aligns to. */\n align?: 'left' | 'right'\n }>(),\n { align: 'right' },\n)\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst dropStyle = ref<Record<string, string>>({})\n\nfunction computePos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const openAbove = spaceBelow < 200 && rect.top > spaceBelow\n\n const style: Record<string, string> = {\n maxHeight: `${Math.min(openAbove ? rect.top - 12 : spaceBelow, 400)}px`,\n }\n\n if (openAbove) {\n style.bottom = `${window.innerHeight - rect.top + 4}px`\n } else {\n style.top = `${rect.bottom + 4}px`\n }\n\n if (props.align === 'right') {\n style.right = `${window.innerWidth - rect.right}px`\n } else {\n style.left = `${rect.left}px`\n }\n\n dropStyle.value = style\n}\n\nfunction toggle() {\n if (!open.value) computePos()\n open.value = !open.value\n}\n\nfunction close() {\n open.value = false\n}\n\ndefineExpose({ close, open })\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!triggerEl.value?.contains(t) && !dropdownEl.value?.contains(t)) close()\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (dropdownEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { close(); return }\n computePos()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') close()\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n document.addEventListener('keydown', onKeydown)\n window.addEventListener('scroll', onScroll, true)\n})\n\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n document.removeEventListener('keydown', onKeydown)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst origin = computed(() =>\n props.align === 'right' ? 'top right' : 'top left',\n)\n</script>\n\n<template>\n <div ref=\"triggerEl\" class=\"inline-block\" @click=\"toggle\">\n <slot name=\"trigger\" :open=\"open\" />\n </div>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-100 ease-out\"\n enter-from-class=\"opacity-0 scale-95\"\n enter-to-class=\"opacity-100 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-75 ease-in\"\n leave-from-class=\"opacity-100 scale-100\"\n leave-to-class=\"opacity-0 scale-95\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] min-w-48 overflow-y-auto overflow-x-hidden rounded-xs bg-surface-container py-1 shadow-elevation-2\"\n :style=\"{ ...dropStyle, transformOrigin: origin }\"\n @click=\"close\"\n >\n <slot />\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n /** Which edge of the trigger the dropdown aligns to. */\n align?: 'left' | 'right'\n }>(),\n { align: 'right' },\n)\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst dropStyle = ref<Record<string, string>>({})\n\nfunction computePos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const openAbove = spaceBelow < 200 && rect.top > spaceBelow\n\n const style: Record<string, string> = {\n maxHeight: `${Math.min(openAbove ? rect.top - 12 : spaceBelow, 400)}px`,\n }\n\n if (openAbove) {\n style.bottom = `${window.innerHeight - rect.top + 4}px`\n } else {\n style.top = `${rect.bottom + 4}px`\n }\n\n if (props.align === 'right') {\n style.right = `${window.innerWidth - rect.right}px`\n } else {\n style.left = `${rect.left}px`\n }\n\n dropStyle.value = style\n}\n\nfunction toggle() {\n if (!open.value) computePos()\n open.value = !open.value\n}\n\nfunction close() {\n open.value = false\n}\n\ndefineExpose({ close, open })\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!triggerEl.value?.contains(t) && !dropdownEl.value?.contains(t)) close()\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (dropdownEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { close(); return }\n computePos()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') close()\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n document.addEventListener('keydown', onKeydown)\n window.addEventListener('scroll', onScroll, true)\n})\n\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n document.removeEventListener('keydown', onKeydown)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst origin = computed(() =>\n props.align === 'right' ? 'top right' : 'top left',\n)\n</script>\n\n<template>\n <div ref=\"triggerEl\" class=\"inline-block\" @click=\"toggle\">\n <slot name=\"trigger\" :open=\"open\" />\n </div>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-100 ease-out\"\n enter-from-class=\"opacity-0 scale-95\"\n enter-to-class=\"opacity-100 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-75 ease-in\"\n leave-from-class=\"opacity-100 scale-100\"\n leave-to-class=\"opacity-0 scale-95\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] min-w-48 overflow-y-auto overflow-x-hidden rounded-xs bg-surface-container py-1 shadow-elevation-2\"\n :style=\"{ ...dropStyle, transformOrigin: origin }\"\n @click=\"close\"\n >\n <slot />\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{ icon?: string }>(), {})\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left text-body-large text-on-surface hover:bg-on-surface/8\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"20\" class=\"text-on-surface-variant\" />\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{ icon?: string }>(), {})\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left text-body-large text-on-surface hover:bg-on-surface/8\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"20\" class=\"text-on-surface-variant\" />\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, onMounted, onUnmounted, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MCheckbox from './MCheckbox.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nexport interface MultiSelectOption {\n label: string\n value: string | number\n disabled?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: (string | number)[]\n options: MultiSelectOption[]\n label?: string\n placeholder?: string\n variant?: 'filled' | 'outlined'\n disabled?: boolean\n error?: string\n hint?: string\n required?: boolean\n leadingIcon?: string\n fieldBg?: string\n searchable?: boolean\n maxChips?: number\n }>(),\n {\n modelValue: () => [],\n variant: 'filled',\n disabled: false,\n required: false,\n searchable: true,\n maxChips: 3,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [(string | number)[]] }>()\n\nconst id = useId()\nconst open = ref(false)\nconst search = ref('')\nconst fieldEl = ref<HTMLElement | null>(null)\nconst { resolvedFieldBg } = useFieldBg(fieldEl, () => props.fieldBg)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst searchInput = ref<HTMLInputElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px', width: '0px' })\n\nconst hasValue = computed(() => props.modelValue.length > 0)\n\nconst filteredOptions = computed(() => {\n if (!search.value) return props.options\n const q = search.value.toLowerCase()\n return props.options.filter((o) => o.label.toLowerCase().includes(q))\n})\n\nconst visibleChips = computed(() =>\n props.modelValue.slice(0, props.maxChips).map((v) => ({\n value: v,\n label: props.options.find((o) => o.value === v)?.label ?? String(v),\n })),\n)\n\nconst overflowCount = computed(() => Math.max(0, props.modelValue.length - props.maxChips))\n\nfunction toggle(value: string | number) {\n const current = props.modelValue\n if (current.includes(value)) {\n emit('update:modelValue', current.filter((v) => v !== value))\n } else {\n emit('update:modelValue', [...current, value])\n }\n}\n\nfunction removeChip(value: string | number, e: Event) {\n e.stopPropagation()\n emit('update:modelValue', props.modelValue.filter((v) => v !== value))\n}\n\nfunction computeDropPos() {\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n dropPos.value = {\n top: `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n }\n}\n\nasync function openDropdown() {\n if (props.disabled) return\n computeDropPos()\n open.value = true\n search.value = ''\n await nextTick()\n searchInput.value?.focus()\n}\n\nfunction close() {\n open.value = false\n search.value = ''\n}\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!fieldEl.value?.contains(t) && !dropdownEl.value?.contains(t)) close()\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (dropdownEl.value?.contains(e.target as Node)) return\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) {\n close()\n return\n }\n computeDropPos()\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst triggerClasses = computed(() => {\n const base = [\n 'flex min-h-[56px] w-full cursor-pointer items-center gap-1.5 flex-wrap',\n 'transition-[border-color,border-width] duration-150',\n props.leadingIcon ? 'pl-12 pr-10' : 'pl-4 pr-10',\n ]\n\n if (props.variant === 'outlined') {\n return [\n ...base,\n 'rounded-sm border bg-transparent py-2',\n open.value\n ? (props.error ? 'border-2 border-error' : 'border-2 border-primary')\n : (props.error ? 'border-error' : 'border-outline hover:border-on-surface'),\n ].join(' ')\n }\n\n return [\n ...base,\n 'rounded-t-sm bg-surface-container-highest border-b pb-2',\n hasValue.value || open.value ? 'pt-7' : 'pt-4',\n open.value\n ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')\n : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),\n ].join(' ')\n})\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? (props.variant === 'outlined' ? 'left-11' : 'left-12')\n : (props.variant === 'outlined' ? 'left-3' : 'left-4')\n\n const floated = props.variant === 'outlined'\n ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'\n : 'top-2 translate-y-0 text-label-small'\n\n const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'\n const active = open.value || hasValue.value\n\n return [\n 'pointer-events-none absolute right-10 truncate transition-all duration-200',\n left,\n active ? floated : unFloated,\n open.value\n ? (props.error ? 'text-error' : 'text-primary')\n : (props.error ? 'text-error' : 'text-on-surface-variant'),\n ].join(' ')\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div\n ref=\"fieldEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <!-- Trigger field -->\n <div\n :id=\"id\"\n :class=\"triggerClasses\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n :aria-expanded=\"open\"\n :aria-haspopup=\"true\"\n @click=\"open ? close() : openDropdown()\"\n @keydown.enter.prevent=\"open ? close() : openDropdown()\"\n @keydown.space.prevent=\"open ? close() : openDropdown()\"\n @keydown.escape=\"close()\"\n >\n <template v-if=\"hasValue\">\n <span\n v-for=\"chip in visibleChips\"\n :key=\"chip.value\"\n class=\"inline-flex items-center gap-1 rounded-full bg-secondary-container px-2 py-0.5 text-label-small text-on-secondary-container\"\n >\n {{ chip.label }}\n <button\n type=\"button\"\n class=\"flex h-4 w-4 items-center justify-center rounded-full hover:bg-on-secondary-container/20\"\n @click=\"removeChip(chip.value, $event)\"\n >\n <MIcon name=\"close\" :size=\"12\" />\n </button>\n </span>\n <span\n v-if=\"overflowCount > 0\"\n class=\"rounded-full bg-surface-container-high px-2 py-0.5 text-label-small text-on-surface-variant\"\n >\n +{{ overflowCount }}\n </span>\n </template>\n <span v-else-if=\"!open\" class=\"text-body-large text-on-surface-variant opacity-0\">\n {{ placeholder }}\n </span>\n </div>\n\n <label :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <div class=\"pointer-events-none absolute right-2 top-7 -translate-y-1/2\">\n <MIcon\n :name=\"open ? 'arrow_drop_up' : 'arrow_drop_down'\"\n :size=\"24\"\n class=\"text-on-surface-variant\"\n />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n\n <!-- Dropdown teleported to body to escape overflow clipping -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n enter-to-class=\"opacity-100 translate-y-0 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-from-class=\"opacity-100 translate-y-0 scale-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] max-h-60 overflow-auto rounded-sm bg-surface-container shadow-elevation-2\"\n :style=\"dropPos\"\n >\n <!-- Search -->\n <div v-if=\"searchable\" class=\"sticky top-0 bg-surface-container px-3 py-2\">\n <div class=\"flex items-center gap-2 rounded-full bg-surface-container-high px-3 py-1.5\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n ref=\"searchInput\"\n v-model=\"search\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\"\n />\n </div>\n </div>\n\n <div class=\"flex flex-col py-1\">\n <label\n v-for=\"opt in filteredOptions\"\n :key=\"opt.value\"\n class=\"flex cursor-pointer items-center gap-3 px-4 py-2 hover:bg-on-surface/8\"\n :class=\"opt.disabled ? 'cursor-not-allowed opacity-38' : ''\"\n >\n <MCheckbox\n :model-value=\"modelValue.includes(opt.value)\"\n :disabled=\"opt.disabled\"\n @update:model-value=\"!opt.disabled && toggle(opt.value)\"\n />\n <span class=\"text-body-large text-on-surface\">{{ opt.label }}</span>\n </label>\n <p\n v-if=\"filteredOptions.length === 0\"\n class=\"px-4 py-3 text-center text-body-small text-on-surface-variant\"\n >\n Sin resultados\n </p>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, onMounted, onUnmounted, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MCheckbox from './MCheckbox.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nexport interface MultiSelectOption {\n label: string\n value: string | number\n disabled?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: (string | number)[]\n options: MultiSelectOption[]\n label?: string\n placeholder?: string\n variant?: 'filled' | 'outlined'\n disabled?: boolean\n error?: string\n hint?: string\n required?: boolean\n leadingIcon?: string\n fieldBg?: string\n searchable?: boolean\n maxChips?: number\n }>(),\n {\n modelValue: () => [],\n variant: 'filled',\n disabled: false,\n required: false,\n searchable: true,\n maxChips: 3,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [(string | number)[]] }>()\n\nconst id = useId()\nconst open = ref(false)\nconst search = ref('')\nconst fieldEl = ref<HTMLElement | null>(null)\nconst { resolvedFieldBg } = useFieldBg(fieldEl, () => props.fieldBg)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst searchInput = ref<HTMLInputElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px', width: '0px' })\n\nconst hasValue = computed(() => props.modelValue.length > 0)\n\nconst filteredOptions = computed(() => {\n if (!search.value) return props.options\n const q = search.value.toLowerCase()\n return props.options.filter((o) => o.label.toLowerCase().includes(q))\n})\n\nconst visibleChips = computed(() =>\n props.modelValue.slice(0, props.maxChips).map((v) => ({\n value: v,\n label: props.options.find((o) => o.value === v)?.label ?? String(v),\n })),\n)\n\nconst overflowCount = computed(() => Math.max(0, props.modelValue.length - props.maxChips))\n\nfunction toggle(value: string | number) {\n const current = props.modelValue\n if (current.includes(value)) {\n emit('update:modelValue', current.filter((v) => v !== value))\n } else {\n emit('update:modelValue', [...current, value])\n }\n}\n\nfunction removeChip(value: string | number, e: Event) {\n e.stopPropagation()\n emit('update:modelValue', props.modelValue.filter((v) => v !== value))\n}\n\nfunction computeDropPos() {\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n dropPos.value = {\n top: `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n }\n}\n\nasync function openDropdown() {\n if (props.disabled) return\n computeDropPos()\n open.value = true\n search.value = ''\n await nextTick()\n searchInput.value?.focus()\n}\n\nfunction close() {\n open.value = false\n search.value = ''\n}\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!fieldEl.value?.contains(t) && !dropdownEl.value?.contains(t)) close()\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (dropdownEl.value?.contains(e.target as Node)) return\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) {\n close()\n return\n }\n computeDropPos()\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst triggerClasses = computed(() => {\n const base = [\n 'flex min-h-[56px] w-full cursor-pointer items-center gap-1.5 flex-wrap',\n 'transition-[border-color,border-width] duration-150',\n props.leadingIcon ? 'pl-12 pr-10' : 'pl-4 pr-10',\n ]\n\n if (props.variant === 'outlined') {\n return [\n ...base,\n 'rounded-sm border bg-transparent py-2',\n open.value\n ? (props.error ? 'border-2 border-error' : 'border-2 border-primary')\n : (props.error ? 'border-error' : 'border-outline hover:border-on-surface'),\n ].join(' ')\n }\n\n return [\n ...base,\n 'rounded-t-sm bg-surface-container-highest border-b pb-2',\n hasValue.value || open.value ? 'pt-7' : 'pt-4',\n open.value\n ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')\n : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),\n ].join(' ')\n})\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? (props.variant === 'outlined' ? 'left-11' : 'left-12')\n : (props.variant === 'outlined' ? 'left-3' : 'left-4')\n\n const floated = props.variant === 'outlined'\n ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'\n : 'top-2 translate-y-0 text-label-small'\n\n const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'\n const active = open.value || hasValue.value\n\n return [\n 'pointer-events-none absolute right-10 truncate transition-all duration-200',\n left,\n active ? floated : unFloated,\n open.value\n ? (props.error ? 'text-error' : 'text-primary')\n : (props.error ? 'text-error' : 'text-on-surface-variant'),\n ].join(' ')\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div\n ref=\"fieldEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <!-- Trigger field -->\n <div\n :id=\"id\"\n :class=\"triggerClasses\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n :aria-expanded=\"open\"\n :aria-haspopup=\"true\"\n @click=\"open ? close() : openDropdown()\"\n @keydown.enter.prevent=\"open ? close() : openDropdown()\"\n @keydown.space.prevent=\"open ? close() : openDropdown()\"\n @keydown.escape=\"close()\"\n >\n <template v-if=\"hasValue\">\n <span\n v-for=\"chip in visibleChips\"\n :key=\"chip.value\"\n class=\"inline-flex items-center gap-1 rounded-full bg-secondary-container px-2 py-0.5 text-label-small text-on-secondary-container\"\n >\n {{ chip.label }}\n <button\n type=\"button\"\n class=\"flex h-4 w-4 items-center justify-center rounded-full hover:bg-on-secondary-container/20\"\n @click=\"removeChip(chip.value, $event)\"\n >\n <MIcon name=\"close\" :size=\"12\" />\n </button>\n </span>\n <span\n v-if=\"overflowCount > 0\"\n class=\"rounded-full bg-surface-container-high px-2 py-0.5 text-label-small text-on-surface-variant\"\n >\n +{{ overflowCount }}\n </span>\n </template>\n <span v-else-if=\"!open\" class=\"text-body-large text-on-surface-variant opacity-0\">\n {{ placeholder }}\n </span>\n </div>\n\n <label :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <div class=\"pointer-events-none absolute right-2 top-7 -translate-y-1/2\">\n <MIcon\n :name=\"open ? 'arrow_drop_up' : 'arrow_drop_down'\"\n :size=\"24\"\n class=\"text-on-surface-variant\"\n />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n\n <!-- Dropdown teleported to body to escape overflow clipping -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n enter-to-class=\"opacity-100 translate-y-0 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-from-class=\"opacity-100 translate-y-0 scale-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] max-h-60 overflow-auto rounded-sm bg-surface-container shadow-elevation-2\"\n :style=\"dropPos\"\n >\n <!-- Search -->\n <div v-if=\"searchable\" class=\"sticky top-0 bg-surface-container px-3 py-2\">\n <div class=\"flex items-center gap-2 rounded-full bg-surface-container-high px-3 py-1.5\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n ref=\"searchInput\"\n v-model=\"search\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\"\n />\n </div>\n </div>\n\n <div class=\"flex flex-col py-1\">\n <label\n v-for=\"opt in filteredOptions\"\n :key=\"opt.value\"\n class=\"flex cursor-pointer items-center gap-3 px-4 py-2 hover:bg-on-surface/8\"\n :class=\"opt.disabled ? 'cursor-not-allowed opacity-38' : ''\"\n >\n <MCheckbox\n :model-value=\"modelValue.includes(opt.value)\"\n :disabled=\"opt.disabled\"\n @update:model-value=\"!opt.disabled && toggle(opt.value)\"\n />\n <span class=\"text-body-large text-on-surface\">{{ opt.label }}</span>\n </label>\n <p\n v-if=\"filteredOptions.length === 0\"\n class=\"px-4 py-3 text-center text-body-small text-on-surface-variant\"\n >\n Sin resultados\n </p>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MBadge from './MBadge.vue'\n\nexport interface NavBarItem {\n value: string | number\n label: string\n icon: string\n badge?: number\n badgeDot?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number\n items: NavBarItem[]\n}>(), {})\n\ndefineEmits<{ 'update:modelValue': [string | number] }>()\n</script>\n\n<template>\n <nav class=\"flex h-20 w-full items-center justify-around border-t border-outline-variant bg-surface-container\">\n <button\n v-for=\"item in items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"group flex flex-1 cursor-pointer flex-col items-center justify-center gap-1 self-stretch transition-colors focus-visible:outline-none\"\n :class=\"\n item.value === modelValue\n ? 'text-on-secondary-container'\n : 'text-on-surface-variant'\n \"\n @click=\"$emit('update:modelValue', item.value)\"\n >\n <!-- Pill indicator with icon -->\n <span\n class=\"inline-flex h-8 items-center justify-center rounded-2xl transition-all duration-200\"\n :class=\"\n item.value === modelValue\n ? 'w-16 bg-secondary-container'\n : 'w-0 bg-secondary-container/0 group-hover:w-16 group-hover:bg-on-surface/8'\n \"\n >\n <MBadge v-if=\"item.badge != null\" :count=\"item.badge\">\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MBadge v-else-if=\"item.badgeDot\" dot>\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MIcon v-else :name=\"item.icon\" :size=\"24\" />\n </span>\n\n <!-- Label -->\n <span\n class=\"text-label-medium transition-[font-weight] duration-150\"\n :class=\"item.value === modelValue ? 'font-bold' : 'font-medium'\"\n >\n {{ item.label }}\n </span>\n </button>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MBadge from './MBadge.vue'\n\nexport interface NavBarItem {\n value: string | number\n label: string\n icon: string\n badge?: number\n badgeDot?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number\n items: NavBarItem[]\n}>(), {})\n\ndefineEmits<{ 'update:modelValue': [string | number] }>()\n</script>\n\n<template>\n <nav class=\"flex h-20 w-full items-center justify-around border-t border-outline-variant bg-surface-container\">\n <button\n v-for=\"item in items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"group flex flex-1 cursor-pointer flex-col items-center justify-center gap-1 self-stretch transition-colors focus-visible:outline-none\"\n :class=\"\n item.value === modelValue\n ? 'text-on-secondary-container'\n : 'text-on-surface-variant'\n \"\n @click=\"$emit('update:modelValue', item.value)\"\n >\n <!-- Pill indicator with icon -->\n <span\n class=\"inline-flex h-8 items-center justify-center rounded-2xl transition-all duration-200\"\n :class=\"\n item.value === modelValue\n ? 'w-16 bg-secondary-container'\n : 'w-0 bg-secondary-container/0 group-hover:w-16 group-hover:bg-on-surface/8'\n \"\n >\n <MBadge v-if=\"item.badge != null\" :count=\"item.badge\">\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MBadge v-else-if=\"item.badgeDot\" dot>\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MIcon v-else :name=\"item.icon\" :size=\"24\" />\n </span>\n\n <!-- Label -->\n <span\n class=\"text-label-medium transition-[font-weight] duration-150\"\n :class=\"item.value === modelValue ? 'font-bold' : 'font-medium'\"\n >\n {{ item.label }}\n </span>\n </button>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface DrawerItem {\n value: string | number\n label: string\n icon?: string\n badge?: string | number\n disabled?: boolean\n}\n\nexport interface DrawerSection {\n title?: string\n items: DrawerItem[]\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: boolean\n selected?: string | number\n sections: DrawerSection[]\n title?: string\n modal?: boolean\n}>(), { modal: true })\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n select: [string | number]\n}>()\n\nfunction close() { emit('update:modelValue', false) }\nfunction select(item: DrawerItem) {\n if (item.disabled) return\n emit('select', item.value)\n if (props.modal) close()\n}\n\nwatch(() => props.modelValue, (open) => {\n if (open) document.body.style.overflow = 'hidden'\n else document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <!-- Modal variant -->\n <Teleport v-if=\"modal\" to=\"body\">\n <Transition name=\"nd\" :duration=\"{ enter: 300, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[100] flex\">\n <!-- Scrim -->\n <div class=\"nd-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <nav class=\"nd-panel relative flex h-full w-72 max-w-[85vw] flex-col bg-surface-container shadow-elevation-3\">\n <!-- Header -->\n <div v-if=\"title || $slots.header\" class=\"shrink-0 px-5 pt-6 pb-2\">\n <slot name=\"header\">\n <h2 class=\"text-title-small font-medium text-on-surface-variant\">{{ title }}</h2>\n </slot>\n </div>\n\n <!-- Sections -->\n <div class=\"flex-1 overflow-y-auto px-3 py-2\">\n <template v-for=\"(section, si) in sections\" :key=\"si\">\n <div v-if=\"si > 0\" class=\"my-2 border-t border-outline-variant\" />\n <p v-if=\"section.title\" class=\"px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant\">\n {{ section.title }}\n </p>\n <button\n v-for=\"item in section.items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-[0.38]'\n : item.value === selected\n ? 'bg-secondary-container text-on-secondary-container'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',\n ]\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"24\" />\n <span class=\"flex-1 text-label-large font-medium\">{{ item.label }}</span>\n <span\n v-if=\"item.badge != null\"\n class=\"text-label-medium text-on-surface-variant\"\n >\n {{ item.badge }}\n </span>\n </button>\n </template>\n </div>\n </nav>\n </div>\n </Transition>\n </Teleport>\n\n <!-- Standard (inline) variant -->\n <nav\n v-else\n class=\"flex h-full w-72 flex-col border-r border-outline-variant bg-surface\"\n >\n <div v-if=\"title || $slots.header\" class=\"shrink-0 px-5 pt-6 pb-2\">\n <slot name=\"header\">\n <h2 class=\"text-title-small font-medium text-on-surface-variant\">{{ title }}</h2>\n </slot>\n </div>\n <div class=\"flex-1 overflow-y-auto px-3 py-2\">\n <template v-for=\"(section, si) in sections\" :key=\"si\">\n <div v-if=\"si > 0\" class=\"my-2 border-t border-outline-variant\" />\n <p v-if=\"section.title\" class=\"px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant\">\n {{ section.title }}\n </p>\n <button\n v-for=\"item in section.items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-[0.38]'\n : item.value === selected\n ? 'bg-secondary-container text-on-secondary-container'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',\n ]\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"24\" />\n <span class=\"flex-1 text-label-large font-medium\">{{ item.label }}</span>\n <span v-if=\"item.badge != null\" class=\"text-label-medium text-on-surface-variant\">\n {{ item.badge }}\n </span>\n </button>\n </template>\n </div>\n </nav>\n</template>\n\n<style scoped>\n.nd-scrim {\n transition: opacity 280ms ease;\n}\n.nd-enter-from .nd-scrim,\n.nd-leave-to .nd-scrim {\n opacity: 0;\n}\n\n.nd-panel {\n transition: transform 300ms cubic-bezier(0.2, 0, 0, 1);\n}\n.nd-enter-from .nd-panel,\n.nd-leave-to .nd-panel {\n transform: translateX(-100%);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface DrawerItem {\n value: string | number\n label: string\n icon?: string\n badge?: string | number\n disabled?: boolean\n}\n\nexport interface DrawerSection {\n title?: string\n items: DrawerItem[]\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: boolean\n selected?: string | number\n sections: DrawerSection[]\n title?: string\n modal?: boolean\n}>(), { modal: true })\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n select: [string | number]\n}>()\n\nfunction close() { emit('update:modelValue', false) }\nfunction select(item: DrawerItem) {\n if (item.disabled) return\n emit('select', item.value)\n if (props.modal) close()\n}\n\nwatch(() => props.modelValue, (open) => {\n if (open) document.body.style.overflow = 'hidden'\n else document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <!-- Modal variant -->\n <Teleport v-if=\"modal\" to=\"body\">\n <Transition name=\"nd\" :duration=\"{ enter: 300, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[100] flex\">\n <!-- Scrim -->\n <div class=\"nd-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <nav class=\"nd-panel relative flex h-full w-72 max-w-[85vw] flex-col bg-surface-container shadow-elevation-3\">\n <!-- Header -->\n <div v-if=\"title || $slots.header\" class=\"shrink-0 px-5 pt-6 pb-2\">\n <slot name=\"header\">\n <h2 class=\"text-title-small font-medium text-on-surface-variant\">{{ title }}</h2>\n </slot>\n </div>\n\n <!-- Sections -->\n <div class=\"flex-1 overflow-y-auto px-3 py-2\">\n <template v-for=\"(section, si) in sections\" :key=\"si\">\n <div v-if=\"si > 0\" class=\"my-2 border-t border-outline-variant\" />\n <p v-if=\"section.title\" class=\"px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant\">\n {{ section.title }}\n </p>\n <button\n v-for=\"item in section.items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-[0.38]'\n : item.value === selected\n ? 'bg-secondary-container text-on-secondary-container'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',\n ]\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"24\" />\n <span class=\"flex-1 text-label-large font-medium\">{{ item.label }}</span>\n <span\n v-if=\"item.badge != null\"\n class=\"text-label-medium text-on-surface-variant\"\n >\n {{ item.badge }}\n </span>\n </button>\n </template>\n </div>\n </nav>\n </div>\n </Transition>\n </Teleport>\n\n <!-- Standard (inline) variant -->\n <nav\n v-else\n class=\"flex h-full w-72 flex-col border-r border-outline-variant bg-surface\"\n >\n <div v-if=\"title || $slots.header\" class=\"shrink-0 px-5 pt-6 pb-2\">\n <slot name=\"header\">\n <h2 class=\"text-title-small font-medium text-on-surface-variant\">{{ title }}</h2>\n </slot>\n </div>\n <div class=\"flex-1 overflow-y-auto px-3 py-2\">\n <template v-for=\"(section, si) in sections\" :key=\"si\">\n <div v-if=\"si > 0\" class=\"my-2 border-t border-outline-variant\" />\n <p v-if=\"section.title\" class=\"px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant\">\n {{ section.title }}\n </p>\n <button\n v-for=\"item in section.items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-[0.38]'\n : item.value === selected\n ? 'bg-secondary-container text-on-secondary-container'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',\n ]\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"24\" />\n <span class=\"flex-1 text-label-large font-medium\">{{ item.label }}</span>\n <span v-if=\"item.badge != null\" class=\"text-label-medium text-on-surface-variant\">\n {{ item.badge }}\n </span>\n </button>\n </template>\n </div>\n </nav>\n</template>\n\n<style scoped>\n.nd-scrim {\n transition: opacity 280ms ease;\n}\n.nd-enter-from .nd-scrim,\n.nd-leave-to .nd-scrim {\n opacity: 0;\n}\n\n.nd-panel {\n transition: transform 300ms cubic-bezier(0.2, 0, 0, 1);\n}\n.nd-enter-from .nd-panel,\n.nd-leave-to .nd-panel {\n transform: translateX(-100%);\n}\n</style>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MBadge from './MBadge.vue'\n\nexport interface NavRailItem {\n value: string | number\n label: string\n icon: string\n badge?: number\n badgeDot?: boolean\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number\n items: NavRailItem[]\n alignment?: 'top' | 'center' | 'bottom'\n}>(), { alignment: 'top' })\n\ndefineEmits<{ 'update:modelValue': [string | number] }>()\n</script>\n\n<template>\n <nav class=\"flex h-full w-20 flex-col items-center border-r border-outline-variant bg-surface\">\n <!-- FAB slot -->\n <div v-if=\"$slots.fab\" class=\"flex shrink-0 items-center justify-center pt-3 pb-2\">\n <slot name=\"fab\" />\n </div>\n\n <!-- Items -->\n <div\n class=\"flex flex-1 flex-col items-center gap-1 py-3\"\n :class=\"{\n 'justify-start': alignment === 'top',\n 'justify-center': alignment === 'center',\n 'justify-end': alignment === 'bottom',\n }\"\n >\n <button\n v-for=\"item in items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"group flex w-full cursor-pointer flex-col items-center justify-center gap-1 px-3 py-2 focus-visible:outline-none\"\n :class=\"item.disabled ? 'cursor-not-allowed opacity-[0.38]' : ''\"\n :disabled=\"item.disabled\"\n @click=\"!item.disabled && $emit('update:modelValue', item.value)\"\n >\n <!-- Pill indicator -->\n <span\n class=\"inline-flex h-8 items-center justify-center rounded-2xl transition-all duration-200\"\n :class=\"\n item.value === modelValue\n ? 'w-14 bg-secondary-container text-on-secondary-container'\n : 'w-14 bg-transparent text-on-surface-variant group-hover:bg-on-surface/8'\n \"\n >\n <MBadge v-if=\"item.badge != null\" :count=\"item.badge\">\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MBadge v-else-if=\"item.badgeDot\" dot>\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MIcon v-else :name=\"item.icon\" :size=\"24\" />\n </span>\n\n <!-- Label -->\n <span\n class=\"max-w-[56px] truncate text-center text-label-medium\"\n :class=\"\n item.value === modelValue\n ? 'font-bold text-on-surface'\n : 'font-medium text-on-surface-variant'\n \"\n >\n {{ item.label }}\n </span>\n </button>\n </div>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MBadge from './MBadge.vue'\n\nexport interface NavRailItem {\n value: string | number\n label: string\n icon: string\n badge?: number\n badgeDot?: boolean\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number\n items: NavRailItem[]\n alignment?: 'top' | 'center' | 'bottom'\n}>(), { alignment: 'top' })\n\ndefineEmits<{ 'update:modelValue': [string | number] }>()\n</script>\n\n<template>\n <nav class=\"flex h-full w-20 flex-col items-center border-r border-outline-variant bg-surface\">\n <!-- FAB slot -->\n <div v-if=\"$slots.fab\" class=\"flex shrink-0 items-center justify-center pt-3 pb-2\">\n <slot name=\"fab\" />\n </div>\n\n <!-- Items -->\n <div\n class=\"flex flex-1 flex-col items-center gap-1 py-3\"\n :class=\"{\n 'justify-start': alignment === 'top',\n 'justify-center': alignment === 'center',\n 'justify-end': alignment === 'bottom',\n }\"\n >\n <button\n v-for=\"item in items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"group flex w-full cursor-pointer flex-col items-center justify-center gap-1 px-3 py-2 focus-visible:outline-none\"\n :class=\"item.disabled ? 'cursor-not-allowed opacity-[0.38]' : ''\"\n :disabled=\"item.disabled\"\n @click=\"!item.disabled && $emit('update:modelValue', item.value)\"\n >\n <!-- Pill indicator -->\n <span\n class=\"inline-flex h-8 items-center justify-center rounded-2xl transition-all duration-200\"\n :class=\"\n item.value === modelValue\n ? 'w-14 bg-secondary-container text-on-secondary-container'\n : 'w-14 bg-transparent text-on-surface-variant group-hover:bg-on-surface/8'\n \"\n >\n <MBadge v-if=\"item.badge != null\" :count=\"item.badge\">\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MBadge v-else-if=\"item.badgeDot\" dot>\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MIcon v-else :name=\"item.icon\" :size=\"24\" />\n </span>\n\n <!-- Label -->\n <span\n class=\"max-w-[56px] truncate text-center text-label-medium\"\n :class=\"\n item.value === modelValue\n ? 'font-bold text-on-surface'\n : 'font-medium text-on-surface-variant'\n \"\n >\n {{ item.label }}\n </span>\n </button>\n </div>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n value?: number;\n indeterminate?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n variant?: \"linear\" | \"wavy\";\n label?: string;\n }>(),\n {\n color: \"primary\",\n variant: \"linear\",\n },\n);\n\nconst isIndeterminate = computed(() => props.indeterminate || props.value === undefined);\nconst clampedValue = computed(() => Math.min(100, Math.max(0, props.value ?? 0)));\n\nconst colorMap: Record<\n \"primary\" | \"secondary\" | \"tertiary\" | \"error\",\n { bar: string; track: string; text: string }\n> = {\n primary: { bar: \"bg-primary\", track: \"bg-primary-container\", text: \"text-primary\" },\n secondary: { bar: \"bg-secondary\", track: \"bg-secondary-container\", text: \"text-secondary\" },\n tertiary: { bar: \"bg-tertiary\", track: \"bg-tertiary-container\", text: \"text-tertiary\" },\n error: { bar: \"bg-error\", track: \"bg-error-container\", text: \"text-error\" },\n};\n\n// ── Wave geometry ────────────────────────────────────────────────────────\n// Smooth sine wave sampled as a single polyline path.\n// Period = 20px (one full up-down cycle). We render a wide strip so that\n// translating by exactly one period gives a seamless infinite scroll.\nconst PERIOD = 20; // px per full sine cycle\nconst AMP = 2.5; // amplitude (bar is 8px tall, mid at 4)\nconst MID = 4;\nconst VIEW_H = 8;\nconst PERIODS = 80; // total cycles → 1600px strip\nconst STEP = 1; // px sampling resolution\n\nconst waveWidth = PERIOD * PERIODS;\n\nconst wavePath = (() => {\n let d = \"\";\n for (let x = 0; x <= waveWidth; x += STEP) {\n const y = MID - AMP * Math.sin((x / PERIOD) * Math.PI * 2);\n d += (x === 0 ? \"M\" : \"L\") + x + \",\" + y.toFixed(2) + \" \";\n }\n return d.trim();\n})();\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <span v-if=\"label\" class=\"text-label-small text-on-surface-variant\">{{ label }}</span>\n\n <!-- ── Linear variant ────────────────────────────────────────────────── -->\n <div\n v-if=\"variant === 'linear'\"\n class=\"relative h-1 w-full overflow-hidden rounded-full\"\n :class=\"colorMap[color].track\"\n role=\"progressbar\"\n :aria-valuenow=\"isIndeterminate ? undefined : clampedValue\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <div\n v-if=\"!isIndeterminate\"\n class=\"h-full rounded-full transition-[width] duration-300 ease-in-out\"\n :class=\"colorMap[color].bar\"\n :style=\"{ width: `${clampedValue}%` }\"\n />\n <div\n v-else\n class=\"absolute inset-y-0 w-2/5 rounded-full animate-[m3-progress-indeterminate_1.6s_ease-in-out_infinite]\"\n :class=\"colorMap[color].bar\"\n />\n </div>\n\n <!-- ── Wavy variant ───────────────────────────────────────────────────── -->\n <div\n v-else\n class=\"relative h-2 w-full overflow-visible\"\n role=\"progressbar\"\n :aria-valuenow=\"isIndeterminate ? undefined : clampedValue\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <!-- DETERMINATE -->\n <template v-if=\"!isIndeterminate\">\n <!-- Active (wavy) portion: clipped to value%, but the wave keeps flowing -->\n <div\n class=\"absolute inset-0 overflow-hidden\"\n :style=\"{\n clipPath: `inset(0 ${100 - clampedValue}% 0 0)`,\n transition: 'clip-path 300ms ease',\n }\"\n >\n <div\n class=\"absolute top-0 left-0 h-full animate-[m3-wave-flow_0.8s_linear_infinite]\"\n :class=\"colorMap[color].text\"\n :style=\"{ width: `${waveWidth}px` }\"\n >\n <svg\n :width=\"waveWidth\"\n :height=\"VIEW_H\"\n :viewBox=\"`0 0 ${waveWidth} ${VIEW_H}`\"\n class=\"h-full\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n :d=\"wavePath\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Inactive (straight track) portion -->\n <div\n class=\"absolute inset-y-0 right-0 flex items-center\"\n :class=\"colorMap[color].track\"\n :style=\"{ left: `calc(${clampedValue}% + 4px)`, transition: 'left 300ms ease' }\"\n style=\"border-radius: 9999px; height: 4px; top: 50%; transform: translateY(-50%)\"\n />\n\n <!-- Stop indicator (dot at the end of the track) -->\n <div\n class=\"absolute rounded-full\"\n :class=\"colorMap[color].bar\"\n :style=\"{\n right: '0',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '4px',\n height: '4px',\n }\"\n />\n </template>\n\n <!-- INDETERMINATE -->\n <div v-else class=\"absolute inset-0 overflow-hidden rounded-full\">\n <div\n class=\"absolute top-0 left-0 h-full animate-[m3-wave-flow_0.9s_linear_infinite]\"\n :class=\"colorMap[color].text\"\n :style=\"{ width: `${waveWidth}px` }\"\n >\n <svg\n :width=\"waveWidth\"\n :height=\"VIEW_H\"\n :viewBox=\"`0 0 ${waveWidth} ${VIEW_H}`\"\n class=\"h-full\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n :d=\"wavePath\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n/* Scroll exactly one period (20px) so the loop is perfectly seamless. */\n@keyframes m3-wave-flow {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-20px);\n }\n}\n\n@keyframes m3-progress-indeterminate {\n 0% {\n left: -40%;\n }\n 100% {\n left: 100%;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .animate-\\[m3-wave-flow_1\\.2s_linear_infinite\\],\n .animate-\\[m3-wave-flow_0\\.9s_linear_infinite\\],\n .animate-\\[m3-progress-indeterminate_1\\.6s_ease-in-out_infinite\\] {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n value?: number;\n indeterminate?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n variant?: \"linear\" | \"wavy\";\n label?: string;\n }>(),\n {\n color: \"primary\",\n variant: \"linear\",\n },\n);\n\nconst isIndeterminate = computed(() => props.indeterminate || props.value === undefined);\nconst clampedValue = computed(() => Math.min(100, Math.max(0, props.value ?? 0)));\n\nconst colorMap: Record<\n \"primary\" | \"secondary\" | \"tertiary\" | \"error\",\n { bar: string; track: string; text: string }\n> = {\n primary: { bar: \"bg-primary\", track: \"bg-primary-container\", text: \"text-primary\" },\n secondary: { bar: \"bg-secondary\", track: \"bg-secondary-container\", text: \"text-secondary\" },\n tertiary: { bar: \"bg-tertiary\", track: \"bg-tertiary-container\", text: \"text-tertiary\" },\n error: { bar: \"bg-error\", track: \"bg-error-container\", text: \"text-error\" },\n};\n\n// ── Wave geometry ────────────────────────────────────────────────────────\n// Smooth sine wave sampled as a single polyline path.\n// Period = 20px (one full up-down cycle). We render a wide strip so that\n// translating by exactly one period gives a seamless infinite scroll.\nconst PERIOD = 20; // px per full sine cycle\nconst AMP = 2.5; // amplitude (bar is 8px tall, mid at 4)\nconst MID = 4;\nconst VIEW_H = 8;\nconst PERIODS = 80; // total cycles → 1600px strip\nconst STEP = 1; // px sampling resolution\n\nconst waveWidth = PERIOD * PERIODS;\n\nconst wavePath = (() => {\n let d = \"\";\n for (let x = 0; x <= waveWidth; x += STEP) {\n const y = MID - AMP * Math.sin((x / PERIOD) * Math.PI * 2);\n d += (x === 0 ? \"M\" : \"L\") + x + \",\" + y.toFixed(2) + \" \";\n }\n return d.trim();\n})();\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <span v-if=\"label\" class=\"text-label-small text-on-surface-variant\">{{ label }}</span>\n\n <!-- ── Linear variant ────────────────────────────────────────────────── -->\n <div\n v-if=\"variant === 'linear'\"\n class=\"relative h-1 w-full overflow-hidden rounded-full\"\n :class=\"colorMap[color].track\"\n role=\"progressbar\"\n :aria-valuenow=\"isIndeterminate ? undefined : clampedValue\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <div\n v-if=\"!isIndeterminate\"\n class=\"h-full rounded-full transition-[width] duration-300 ease-in-out\"\n :class=\"colorMap[color].bar\"\n :style=\"{ width: `${clampedValue}%` }\"\n />\n <div\n v-else\n class=\"absolute inset-y-0 w-2/5 rounded-full animate-[m3-progress-indeterminate_1.6s_ease-in-out_infinite]\"\n :class=\"colorMap[color].bar\"\n />\n </div>\n\n <!-- ── Wavy variant ───────────────────────────────────────────────────── -->\n <div\n v-else\n class=\"relative h-2 w-full overflow-visible\"\n role=\"progressbar\"\n :aria-valuenow=\"isIndeterminate ? undefined : clampedValue\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <!-- DETERMINATE -->\n <template v-if=\"!isIndeterminate\">\n <!-- Active (wavy) portion: clipped to value%, but the wave keeps flowing -->\n <div\n class=\"absolute inset-0 overflow-hidden\"\n :style=\"{\n clipPath: `inset(0 ${100 - clampedValue}% 0 0)`,\n transition: 'clip-path 300ms ease',\n }\"\n >\n <div\n class=\"absolute top-0 left-0 h-full animate-[m3-wave-flow_0.8s_linear_infinite]\"\n :class=\"colorMap[color].text\"\n :style=\"{ width: `${waveWidth}px` }\"\n >\n <svg\n :width=\"waveWidth\"\n :height=\"VIEW_H\"\n :viewBox=\"`0 0 ${waveWidth} ${VIEW_H}`\"\n class=\"h-full\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n :d=\"wavePath\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Inactive (straight track) portion -->\n <div\n class=\"absolute inset-y-0 right-0 flex items-center\"\n :class=\"colorMap[color].track\"\n :style=\"{ left: `calc(${clampedValue}% + 4px)`, transition: 'left 300ms ease' }\"\n style=\"border-radius: 9999px; height: 4px; top: 50%; transform: translateY(-50%)\"\n />\n\n <!-- Stop indicator (dot at the end of the track) -->\n <div\n class=\"absolute rounded-full\"\n :class=\"colorMap[color].bar\"\n :style=\"{\n right: '0',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '4px',\n height: '4px',\n }\"\n />\n </template>\n\n <!-- INDETERMINATE -->\n <div v-else class=\"absolute inset-0 overflow-hidden rounded-full\">\n <div\n class=\"absolute top-0 left-0 h-full animate-[m3-wave-flow_0.9s_linear_infinite]\"\n :class=\"colorMap[color].text\"\n :style=\"{ width: `${waveWidth}px` }\"\n >\n <svg\n :width=\"waveWidth\"\n :height=\"VIEW_H\"\n :viewBox=\"`0 0 ${waveWidth} ${VIEW_H}`\"\n class=\"h-full\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n :d=\"wavePath\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n/* Scroll exactly one period (20px) so the loop is perfectly seamless. */\n@keyframes m3-wave-flow {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-20px);\n }\n}\n\n@keyframes m3-progress-indeterminate {\n 0% {\n left: -40%;\n }\n 100% {\n left: 100%;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .animate-\\[m3-wave-flow_1\\.2s_linear_infinite\\],\n .animate-\\[m3-wave-flow_0\\.9s_linear_infinite\\],\n .animate-\\[m3-progress-indeterminate_1\\.6s_ease-in-out_infinite\\] {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: unknown;\n value: unknown;\n label?: string;\n disabled?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n }>(),\n { disabled: false, color: \"primary\" },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [unknown] }>();\nconst id = useId();\nconst isChecked = computed(() => props.modelValue === props.value);\n\n// Ring + dot color when checked, applied via currentColor on the SVG.\nconst checkedColor: Record<string, string> = {\n primary: \"text-primary\",\n secondary: \"text-secondary\",\n tertiary: \"text-tertiary\",\n error: \"text-error\",\n};\n</script>\n\n<template>\n <label\n :for=\"id\"\n class=\"inline-flex items-center gap-3 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span class=\"relative flex h-5 w-5 shrink-0\">\n <input\n :id=\"id\"\n type=\"radio\"\n class=\"sr-only\"\n :checked=\"isChecked\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', value)\"\n />\n\n <!--\n SVG radio: vector circles sharing center (10,10) stay round + concentric\n at any zoom. Outer ring uses r=8 (not 9) so the 2px stroke (7..9) leaves\n ~1px of clearance to the viewBox edge — prevents the border getting\n clipped at certain zoom levels.\n -->\n <svg\n viewBox=\"0 0 20 20\"\n class=\"h-full w-full transition-colors duration-150\"\n :class=\"isChecked ? checkedColor[color] : 'text-on-surface-variant'\"\n aria-hidden=\"true\"\n >\n <circle cx=\"10\" cy=\"10\" r=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n <!--\n Dot scaled via CSS, but the transform-origin is pinned to the circle's\n own bounding box center (transform-box: fill-box). Without this, the SVG\n element origin is (0,0) of the viewBox, so scale() grows from a corner\n and the dot visibly slides to the center. fill-box fixes the origin to\n the dot itself, so it grows symmetrically in place.\n -->\n <circle\n class=\"m3-radio-dot\"\n :class=\"{ 'is-checked': isChecked }\"\n cx=\"10\"\n cy=\"10\"\n r=\"4.5\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n\n <span v-if=\"label\" class=\"text-body-large text-on-surface\">{{ label }}</span>\n </label>\n</template>\n\n<style scoped>\n.m3-radio-dot {\n transform: scale(0);\n transform-box: fill-box;\n transform-origin: center;\n transition: transform 150ms ease;\n}\n.m3-radio-dot.is-checked {\n transform: scale(1);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: unknown;\n value: unknown;\n label?: string;\n disabled?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n }>(),\n { disabled: false, color: \"primary\" },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [unknown] }>();\nconst id = useId();\nconst isChecked = computed(() => props.modelValue === props.value);\n\n// Ring + dot color when checked, applied via currentColor on the SVG.\nconst checkedColor: Record<string, string> = {\n primary: \"text-primary\",\n secondary: \"text-secondary\",\n tertiary: \"text-tertiary\",\n error: \"text-error\",\n};\n</script>\n\n<template>\n <label\n :for=\"id\"\n class=\"inline-flex items-center gap-3 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span class=\"relative flex h-5 w-5 shrink-0\">\n <input\n :id=\"id\"\n type=\"radio\"\n class=\"sr-only\"\n :checked=\"isChecked\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', value)\"\n />\n\n <!--\n SVG radio: vector circles sharing center (10,10) stay round + concentric\n at any zoom. Outer ring uses r=8 (not 9) so the 2px stroke (7..9) leaves\n ~1px of clearance to the viewBox edge — prevents the border getting\n clipped at certain zoom levels.\n -->\n <svg\n viewBox=\"0 0 20 20\"\n class=\"h-full w-full transition-colors duration-150\"\n :class=\"isChecked ? checkedColor[color] : 'text-on-surface-variant'\"\n aria-hidden=\"true\"\n >\n <circle cx=\"10\" cy=\"10\" r=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n <!--\n Dot scaled via CSS, but the transform-origin is pinned to the circle's\n own bounding box center (transform-box: fill-box). Without this, the SVG\n element origin is (0,0) of the viewBox, so scale() grows from a corner\n and the dot visibly slides to the center. fill-box fixes the origin to\n the dot itself, so it grows symmetrically in place.\n -->\n <circle\n class=\"m3-radio-dot\"\n :class=\"{ 'is-checked': isChecked }\"\n cx=\"10\"\n cy=\"10\"\n r=\"4.5\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n\n <span v-if=\"label\" class=\"text-body-large text-on-surface\">{{ label }}</span>\n </label>\n</template>\n\n<style scoped>\n.m3-radio-dot {\n transform: scale(0);\n transform-box: fill-box;\n transform-origin: center;\n transition: transform 150ms ease;\n}\n.m3-radio-dot.is-checked {\n transform: scale(1);\n}\n</style>\n","<script setup lang=\"ts\">\nimport MRadio from './MRadio.vue'\n\ninterface Option {\n label: string\n value: unknown\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: unknown\n options: Option[]\n label?: string\n direction?: 'column' | 'row'\n disabled?: boolean\n color?: 'primary' | 'secondary' | 'tertiary' | 'error'\n}>(), { direction: 'column', disabled: false, color: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [unknown] }>()\n</script>\n\n<template>\n <div class=\"flex flex-col gap-2\">\n <span v-if=\"label\" class=\"text-label-large text-on-surface-variant\">{{ label }}</span>\n <div\n class=\"flex gap-4\"\n :class=\"direction === 'row' ? 'flex-row flex-wrap' : 'flex-col'\"\n >\n <MRadio\n v-for=\"opt in options\"\n :key=\"String(opt.value)\"\n :model-value=\"modelValue\"\n :value=\"opt.value\"\n :label=\"opt.label\"\n :color=\"color\"\n :disabled=\"disabled || !!opt.disabled\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MRadio from './MRadio.vue'\n\ninterface Option {\n label: string\n value: unknown\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: unknown\n options: Option[]\n label?: string\n direction?: 'column' | 'row'\n disabled?: boolean\n color?: 'primary' | 'secondary' | 'tertiary' | 'error'\n}>(), { direction: 'column', disabled: false, color: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [unknown] }>()\n</script>\n\n<template>\n <div class=\"flex flex-col gap-2\">\n <span v-if=\"label\" class=\"text-label-large text-on-surface-variant\">{{ label }}</span>\n <div\n class=\"flex gap-4\"\n :class=\"direction === 'row' ? 'flex-row flex-wrap' : 'flex-col'\"\n >\n <MRadio\n v-for=\"opt in options\"\n :key=\"String(opt.value)\"\n :model-value=\"modelValue\"\n :value=\"opt.value\"\n :label=\"opt.label\"\n :color=\"color\"\n :disabled=\"disabled || !!opt.disabled\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n modelValue: number\n max?: number\n size?: number\n readonly?: boolean\n disabled?: boolean\n color?: string\n icon?: string\n halfIncrements?: boolean\n}>(), { max: 5, size: 28, color: 'primary', icon: 'star', halfIncrements: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [number] }>()\n\nconst hovered = ref(-1)\n\nconst colorClass = computed(() => {\n const map: Record<string, string> = {\n primary: 'text-primary',\n secondary: 'text-secondary',\n tertiary: 'text-tertiary',\n error: 'text-error',\n }\n return map[props.color] || ''\n})\n\nconst customStyle = computed(() => {\n if (['primary', 'secondary', 'tertiary', 'error'].includes(props.color)) return undefined\n return { color: props.color }\n})\n\nfunction valueAt(index: number, e?: MouseEvent) {\n if (!props.halfIncrements) return index + 1\n if (!e) return index + 1\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()\n const half = (e.clientX - rect.left) < rect.width / 2\n return half ? index + 0.5 : index + 1\n}\n\nfunction onClick(index: number, e: MouseEvent) {\n if (props.readonly || props.disabled) return\n const v = valueAt(index, e)\n emit('update:modelValue', v === props.modelValue ? 0 : v)\n}\n\nfunction onMove(index: number, e: MouseEvent) {\n if (props.readonly || props.disabled) return\n hovered.value = valueAt(index, e)\n}\n\nfunction onLeave() {\n hovered.value = -1\n}\n\nfunction iconName(index: number) {\n const active = hovered.value >= 0 ? hovered.value : props.modelValue\n if (index + 1 <= active) return props.icon\n if (props.halfIncrements && index + 0.5 <= active) return props.icon + '_half'\n return props.icon + '_border' // outlined variant not available for all icons\n}\n\nfunction isFilled(index: number) {\n const active = hovered.value >= 0 ? hovered.value : props.modelValue\n return index + 1 <= active || (props.halfIncrements && index + 0.5 <= active)\n}\n</script>\n\n<template>\n <div\n class=\"inline-flex items-center gap-0.5\"\n :class=\"disabled ? 'opacity-[0.38]' : ''\"\n @mouseleave=\"onLeave\"\n >\n <button\n v-for=\"i in max\"\n :key=\"i\"\n type=\"button\"\n class=\"relative inline-flex items-center justify-center rounded-full p-0.5 transition-transform duration-100\"\n :class=\"[\n readonly || disabled ? 'cursor-default' : 'cursor-pointer hover:scale-110',\n ]\"\n :style=\"customStyle\"\n :disabled=\"disabled\"\n @click=\"onClick(i - 1, $event)\"\n @mousemove=\"onMove(i - 1, $event)\"\n >\n <!-- Filled star -->\n <MIcon\n v-if=\"isFilled(i - 1)\"\n :name=\"icon\"\n :size=\"size\"\n :class=\"colorClass\"\n :style=\"customStyle\"\n style=\"font-variation-settings: 'FILL' 1\"\n />\n <!-- Empty star -->\n <MIcon\n v-else\n :name=\"icon\"\n :size=\"size\"\n class=\"text-on-surface-variant/40\"\n />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n modelValue: number\n max?: number\n size?: number\n readonly?: boolean\n disabled?: boolean\n color?: string\n icon?: string\n halfIncrements?: boolean\n}>(), { max: 5, size: 28, color: 'primary', icon: 'star', halfIncrements: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [number] }>()\n\nconst hovered = ref(-1)\n\nconst colorClass = computed(() => {\n const map: Record<string, string> = {\n primary: 'text-primary',\n secondary: 'text-secondary',\n tertiary: 'text-tertiary',\n error: 'text-error',\n }\n return map[props.color] || ''\n})\n\nconst customStyle = computed(() => {\n if (['primary', 'secondary', 'tertiary', 'error'].includes(props.color)) return undefined\n return { color: props.color }\n})\n\nfunction valueAt(index: number, e?: MouseEvent) {\n if (!props.halfIncrements) return index + 1\n if (!e) return index + 1\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()\n const half = (e.clientX - rect.left) < rect.width / 2\n return half ? index + 0.5 : index + 1\n}\n\nfunction onClick(index: number, e: MouseEvent) {\n if (props.readonly || props.disabled) return\n const v = valueAt(index, e)\n emit('update:modelValue', v === props.modelValue ? 0 : v)\n}\n\nfunction onMove(index: number, e: MouseEvent) {\n if (props.readonly || props.disabled) return\n hovered.value = valueAt(index, e)\n}\n\nfunction onLeave() {\n hovered.value = -1\n}\n\nfunction iconName(index: number) {\n const active = hovered.value >= 0 ? hovered.value : props.modelValue\n if (index + 1 <= active) return props.icon\n if (props.halfIncrements && index + 0.5 <= active) return props.icon + '_half'\n return props.icon + '_border' // outlined variant not available for all icons\n}\n\nfunction isFilled(index: number) {\n const active = hovered.value >= 0 ? hovered.value : props.modelValue\n return index + 1 <= active || (props.halfIncrements && index + 0.5 <= active)\n}\n</script>\n\n<template>\n <div\n class=\"inline-flex items-center gap-0.5\"\n :class=\"disabled ? 'opacity-[0.38]' : ''\"\n @mouseleave=\"onLeave\"\n >\n <button\n v-for=\"i in max\"\n :key=\"i\"\n type=\"button\"\n class=\"relative inline-flex items-center justify-center rounded-full p-0.5 transition-transform duration-100\"\n :class=\"[\n readonly || disabled ? 'cursor-default' : 'cursor-pointer hover:scale-110',\n ]\"\n :style=\"customStyle\"\n :disabled=\"disabled\"\n @click=\"onClick(i - 1, $event)\"\n @mousemove=\"onMove(i - 1, $event)\"\n >\n <!-- Filled star -->\n <MIcon\n v-if=\"isFilled(i - 1)\"\n :name=\"icon\"\n :size=\"size\"\n :class=\"colorClass\"\n :style=\"customStyle\"\n style=\"font-variation-settings: 'FILL' 1\"\n />\n <!-- Empty star -->\n <MIcon\n v-else\n :name=\"icon\"\n :size=\"size\"\n class=\"text-on-surface-variant/40\"\n />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n status: 'success' | 'error' | 'warning' | 'info' | '404' | '403' | '500'\n title?: string\n description?: string\n}>(), {})\n\nconst config = computed(() => {\n switch (props.status) {\n case 'success': return { icon: 'check_circle', bg: 'bg-success-container', text: 'text-on-success-container', defaultTitle: 'Operación exitosa', defaultDesc: 'La acción se completó correctamente.' }\n case 'error': return { icon: 'error', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Algo salió mal', defaultDesc: 'Ocurrió un error inesperado. Inténtalo de nuevo.' }\n case 'warning': return { icon: 'warning', bg: 'bg-tertiary-container', text: 'text-on-tertiary-container', defaultTitle: 'Atención', defaultDesc: 'Hay algo que requiere tu atención.' }\n case 'info': return { icon: 'info', bg: 'bg-primary-container', text: 'text-on-primary-container', defaultTitle: 'Información', defaultDesc: '' }\n case '404': return { icon: 'search_off', bg: 'bg-surface-container-high', text: 'text-on-surface-variant', defaultTitle: 'Página no encontrada', defaultDesc: 'La página que buscas no existe o fue movida.' }\n case '403': return { icon: 'lock', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Acceso denegado', defaultDesc: 'No tienes permisos para ver este recurso.' }\n case '500': return { icon: 'cloud_off', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Error del servidor', defaultDesc: 'El servidor no pudo procesar la solicitud.' }\n default: return { icon: 'info', bg: 'bg-surface-container-high', text: 'text-on-surface-variant', defaultTitle: '', defaultDesc: '' }\n }\n})\n\nconst httpCode = computed(() => {\n if (props.status === '404' || props.status === '403' || props.status === '500') return props.status\n return null\n})\n</script>\n\n<template>\n <div class=\"flex flex-col items-center justify-center gap-4 py-14 text-center\">\n <!-- HTTP code -->\n <span v-if=\"httpCode\" class=\"text-display-small font-medium text-on-surface-variant/30\">\n {{ httpCode }}\n </span>\n\n <!-- Icon -->\n <div class=\"flex h-20 w-20 items-center justify-center rounded-full\" :class=\"[config.bg, config.text]\">\n <MIcon :name=\"config.icon\" :size=\"40\" />\n </div>\n\n <!-- Title -->\n <h2 class=\"text-headline-small font-medium text-on-surface\">\n {{ title ?? config.defaultTitle }}\n </h2>\n\n <!-- Description -->\n <p v-if=\"description ?? config.defaultDesc\" class=\"max-w-md text-body-large text-on-surface-variant\">\n {{ description ?? config.defaultDesc }}\n </p>\n\n <!-- Actions slot -->\n <div v-if=\"$slots.actions\" class=\"mt-2 flex flex-wrap items-center justify-center gap-3\">\n <slot name=\"actions\" />\n </div>\n\n <!-- Extra content -->\n <div v-if=\"$slots.default\" class=\"mt-2\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n status: 'success' | 'error' | 'warning' | 'info' | '404' | '403' | '500'\n title?: string\n description?: string\n}>(), {})\n\nconst config = computed(() => {\n switch (props.status) {\n case 'success': return { icon: 'check_circle', bg: 'bg-success-container', text: 'text-on-success-container', defaultTitle: 'Operación exitosa', defaultDesc: 'La acción se completó correctamente.' }\n case 'error': return { icon: 'error', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Algo salió mal', defaultDesc: 'Ocurrió un error inesperado. Inténtalo de nuevo.' }\n case 'warning': return { icon: 'warning', bg: 'bg-tertiary-container', text: 'text-on-tertiary-container', defaultTitle: 'Atención', defaultDesc: 'Hay algo que requiere tu atención.' }\n case 'info': return { icon: 'info', bg: 'bg-primary-container', text: 'text-on-primary-container', defaultTitle: 'Información', defaultDesc: '' }\n case '404': return { icon: 'search_off', bg: 'bg-surface-container-high', text: 'text-on-surface-variant', defaultTitle: 'Página no encontrada', defaultDesc: 'La página que buscas no existe o fue movida.' }\n case '403': return { icon: 'lock', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Acceso denegado', defaultDesc: 'No tienes permisos para ver este recurso.' }\n case '500': return { icon: 'cloud_off', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Error del servidor', defaultDesc: 'El servidor no pudo procesar la solicitud.' }\n default: return { icon: 'info', bg: 'bg-surface-container-high', text: 'text-on-surface-variant', defaultTitle: '', defaultDesc: '' }\n }\n})\n\nconst httpCode = computed(() => {\n if (props.status === '404' || props.status === '403' || props.status === '500') return props.status\n return null\n})\n</script>\n\n<template>\n <div class=\"flex flex-col items-center justify-center gap-4 py-14 text-center\">\n <!-- HTTP code -->\n <span v-if=\"httpCode\" class=\"text-display-small font-medium text-on-surface-variant/30\">\n {{ httpCode }}\n </span>\n\n <!-- Icon -->\n <div class=\"flex h-20 w-20 items-center justify-center rounded-full\" :class=\"[config.bg, config.text]\">\n <MIcon :name=\"config.icon\" :size=\"40\" />\n </div>\n\n <!-- Title -->\n <h2 class=\"text-headline-small font-medium text-on-surface\">\n {{ title ?? config.defaultTitle }}\n </h2>\n\n <!-- Description -->\n <p v-if=\"description ?? config.defaultDesc\" class=\"max-w-md text-body-large text-on-surface-variant\">\n {{ description ?? config.defaultDesc }}\n </p>\n\n <!-- Actions slot -->\n <div v-if=\"$slots.actions\" class=\"mt-2 flex flex-wrap items-center justify-center gap-3\">\n <slot name=\"actions\" />\n </div>\n\n <!-- Extra content -->\n <div v-if=\"$slots.default\" class=\"mt-2\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface SchedulerEvent {\n id: string | number\n title: string\n start: string\n end: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n}\n\nconst props = withDefaults(\n defineProps<{\n events?: SchedulerEvent[]\n view?: 'week' | 'day'\n startHour?: number\n endHour?: number\n locale?: string\n }>(),\n {\n events: () => [],\n view: 'week',\n startHour: 7,\n endHour: 22,\n locale: 'es-ES',\n },\n)\n\nconst emit = defineEmits<{\n eventClick: [SchedulerEvent]\n slotClick: [{ date: string; hour: number }]\n}>()\n\nconst currentDate = ref(new Date())\nconst currentView = ref(props.view)\n\nconst hours = computed(() =>\n Array.from({ length: props.endHour - props.startHour }, (_, i) => props.startHour + i),\n)\n\nfunction startOfWeek(d: Date) {\n const dt = new Date(d)\n const day = dt.getDay()\n const diff = day === 0 ? -6 : 1 - day\n dt.setDate(dt.getDate() + diff)\n dt.setHours(0, 0, 0, 0)\n return dt\n}\n\nconst weekDays = computed(() => {\n const start = startOfWeek(currentDate.value)\n return Array.from({ length: 7 }, (_, i) => {\n const d = new Date(start)\n d.setDate(d.getDate() + i)\n return d\n })\n})\n\nconst visibleDays = computed(() =>\n currentView.value === 'day' ? [currentDate.value] : weekDays.value,\n)\n\nconst todayIso = (() => {\n const d = new Date()\n return fmt(d)\n})()\n\nfunction fmt(d: Date) {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n}\n\nconst dayFormat = new Intl.DateTimeFormat(props.locale, { weekday: 'short' })\nconst dateFormat = new Intl.DateTimeFormat(props.locale, { day: 'numeric' })\n\nconst headerLabel = computed(() => {\n if (currentView.value === 'day') {\n return new Intl.DateTimeFormat(props.locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })\n .format(currentDate.value)\n }\n const start = weekDays.value[0]!\n const end = weekDays.value[6]!\n const f = new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short' })\n return `${f.format(start)} – ${f.format(end)}, ${end.getFullYear()}`\n})\n\nfunction navigate(delta: number) {\n const d = new Date(currentDate.value)\n if (currentView.value === 'week') d.setDate(d.getDate() + delta * 7)\n else d.setDate(d.getDate() + delta)\n currentDate.value = d\n}\n\nfunction goToday() { currentDate.value = new Date() }\n\nfunction eventsForDayHour(day: Date, hour: number) {\n const dayStr = fmt(day)\n return props.events.filter((ev) => {\n const start = new Date(ev.start)\n const end = new Date(ev.end)\n const evDay = fmt(start)\n if (evDay !== dayStr) return false\n const evStartHour = start.getHours()\n const evEndHour = end.getHours() + (end.getMinutes() > 0 ? 1 : 0)\n return hour >= evStartHour && hour < evEndHour\n })\n}\n\nfunction isEventStart(ev: SchedulerEvent, hour: number) {\n return new Date(ev.start).getHours() === hour\n}\n\nfunction eventDuration(ev: SchedulerEvent) {\n const start = new Date(ev.start)\n const end = new Date(ev.end)\n return Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 3600000))\n}\n\nfunction timeLabel(ev: SchedulerEvent) {\n const f = new Intl.DateTimeFormat(props.locale, { hour: '2-digit', minute: '2-digit' })\n return `${f.format(new Date(ev.start))} – ${f.format(new Date(ev.end))}`\n}\n\nconst eventColors: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container border-primary/30',\n secondary: 'bg-secondary-container text-on-secondary-container border-secondary/30',\n tertiary: 'bg-tertiary-container text-on-tertiary-container border-tertiary/30',\n error: 'bg-error-container text-on-error-container border-error/30',\n success: 'bg-success-container text-on-success-container border-success/30',\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-3\">\n <div class=\"flex items-center gap-1\">\n <MIconButton icon=\"chevron_left\" label=\"Anterior\" :size=\"36\" @click=\"navigate(-1)\" />\n <MIconButton icon=\"chevron_right\" label=\"Siguiente\" :size=\"36\" @click=\"navigate(1)\" />\n <button\n type=\"button\"\n class=\"ml-2 cursor-pointer rounded-full border border-outline px-3 py-1 text-label-medium text-on-surface transition-colors hover:bg-on-surface/8\"\n @click=\"goToday\"\n >\n Hoy\n </button>\n </div>\n\n <h3 class=\"text-title-medium font-medium capitalize text-on-surface\">{{ headerLabel }}</h3>\n\n <div class=\"flex rounded-full bg-surface-container-high p-0.5\">\n <button\n v-for=\"v in (['day', 'week'] as const)\"\n :key=\"v\"\n type=\"button\"\n class=\"cursor-pointer rounded-full px-3 py-1 text-label-medium transition-all duration-150\"\n :class=\"currentView === v ? 'bg-secondary-container text-on-secondary-container shadow-elevation-1' : 'text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"currentView = v\"\n >\n {{ v === 'day' ? 'Día' : 'Semana' }}\n </button>\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"overflow-auto\">\n <table class=\"w-full border-collapse\">\n <!-- Day headers -->\n <thead>\n <tr>\n <th class=\"sticky top-0 z-10 w-16 border-b border-r border-outline-variant bg-surface-container p-2\" />\n <th\n v-for=\"day in visibleDays\"\n :key=\"fmt(day)\"\n class=\"sticky top-0 z-10 border-b border-r border-outline-variant bg-surface-container px-2 py-2 text-center last:border-r-0\"\n :class=\"fmt(day) === todayIso ? 'bg-primary-container/30' : ''\"\n >\n <div class=\"text-label-small uppercase text-on-surface-variant\">{{ dayFormat.format(day) }}</div>\n <div\n class=\"mx-auto mt-0.5 flex h-8 w-8 items-center justify-center rounded-full text-title-medium\"\n :class=\"fmt(day) === todayIso ? 'bg-primary text-on-primary font-medium' : 'text-on-surface'\"\n >\n {{ dateFormat.format(day) }}\n </div>\n </th>\n </tr>\n </thead>\n\n <tbody>\n <tr v-for=\"hour in hours\" :key=\"hour\">\n <!-- Hour label -->\n <td class=\"w-16 border-r border-b border-outline-variant/50 p-0 pr-2 text-right align-top\">\n <span class=\"relative -top-2.5 text-label-small text-on-surface-variant\">\n {{ String(hour).padStart(2, '0') }}:00\n </span>\n </td>\n\n <!-- Day cells -->\n <td\n v-for=\"day in visibleDays\"\n :key=\"fmt(day)\"\n class=\"relative h-14 border-r border-b border-outline-variant/50 p-0 last:border-r-0\"\n :class=\"fmt(day) === todayIso ? 'bg-primary-container/[0.05]' : ''\"\n @click=\"emit('slotClick', { date: fmt(day), hour })\"\n >\n <template v-for=\"ev in eventsForDayHour(day, hour)\" :key=\"ev.id\">\n <button\n v-if=\"isEventStart(ev, hour)\"\n type=\"button\"\n class=\"absolute inset-x-0.5 top-0.5 z-[5] cursor-pointer overflow-hidden rounded border-l-[3px] px-2 py-1 text-left transition-opacity hover:opacity-90\"\n :class=\"eventColors[ev.color ?? 'primary']\"\n :style=\"{ height: `calc(${eventDuration(ev) * 100}% - 4px)` }\"\n @click.stop=\"emit('eventClick', ev)\"\n >\n <p class=\"truncate text-label-small font-medium\">{{ ev.title }}</p>\n <p class=\"truncate text-label-small opacity-70\">{{ timeLabel(ev) }}</p>\n </button>\n </template>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface SchedulerEvent {\n id: string | number\n title: string\n start: string\n end: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n}\n\nconst props = withDefaults(\n defineProps<{\n events?: SchedulerEvent[]\n view?: 'week' | 'day'\n startHour?: number\n endHour?: number\n locale?: string\n }>(),\n {\n events: () => [],\n view: 'week',\n startHour: 7,\n endHour: 22,\n locale: 'es-ES',\n },\n)\n\nconst emit = defineEmits<{\n eventClick: [SchedulerEvent]\n slotClick: [{ date: string; hour: number }]\n}>()\n\nconst currentDate = ref(new Date())\nconst currentView = ref(props.view)\n\nconst hours = computed(() =>\n Array.from({ length: props.endHour - props.startHour }, (_, i) => props.startHour + i),\n)\n\nfunction startOfWeek(d: Date) {\n const dt = new Date(d)\n const day = dt.getDay()\n const diff = day === 0 ? -6 : 1 - day\n dt.setDate(dt.getDate() + diff)\n dt.setHours(0, 0, 0, 0)\n return dt\n}\n\nconst weekDays = computed(() => {\n const start = startOfWeek(currentDate.value)\n return Array.from({ length: 7 }, (_, i) => {\n const d = new Date(start)\n d.setDate(d.getDate() + i)\n return d\n })\n})\n\nconst visibleDays = computed(() =>\n currentView.value === 'day' ? [currentDate.value] : weekDays.value,\n)\n\nconst todayIso = (() => {\n const d = new Date()\n return fmt(d)\n})()\n\nfunction fmt(d: Date) {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n}\n\nconst dayFormat = new Intl.DateTimeFormat(props.locale, { weekday: 'short' })\nconst dateFormat = new Intl.DateTimeFormat(props.locale, { day: 'numeric' })\n\nconst headerLabel = computed(() => {\n if (currentView.value === 'day') {\n return new Intl.DateTimeFormat(props.locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })\n .format(currentDate.value)\n }\n const start = weekDays.value[0]!\n const end = weekDays.value[6]!\n const f = new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short' })\n return `${f.format(start)} – ${f.format(end)}, ${end.getFullYear()}`\n})\n\nfunction navigate(delta: number) {\n const d = new Date(currentDate.value)\n if (currentView.value === 'week') d.setDate(d.getDate() + delta * 7)\n else d.setDate(d.getDate() + delta)\n currentDate.value = d\n}\n\nfunction goToday() { currentDate.value = new Date() }\n\nfunction eventsForDayHour(day: Date, hour: number) {\n const dayStr = fmt(day)\n return props.events.filter((ev) => {\n const start = new Date(ev.start)\n const end = new Date(ev.end)\n const evDay = fmt(start)\n if (evDay !== dayStr) return false\n const evStartHour = start.getHours()\n const evEndHour = end.getHours() + (end.getMinutes() > 0 ? 1 : 0)\n return hour >= evStartHour && hour < evEndHour\n })\n}\n\nfunction isEventStart(ev: SchedulerEvent, hour: number) {\n return new Date(ev.start).getHours() === hour\n}\n\nfunction eventDuration(ev: SchedulerEvent) {\n const start = new Date(ev.start)\n const end = new Date(ev.end)\n return Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 3600000))\n}\n\nfunction timeLabel(ev: SchedulerEvent) {\n const f = new Intl.DateTimeFormat(props.locale, { hour: '2-digit', minute: '2-digit' })\n return `${f.format(new Date(ev.start))} – ${f.format(new Date(ev.end))}`\n}\n\nconst eventColors: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container border-primary/30',\n secondary: 'bg-secondary-container text-on-secondary-container border-secondary/30',\n tertiary: 'bg-tertiary-container text-on-tertiary-container border-tertiary/30',\n error: 'bg-error-container text-on-error-container border-error/30',\n success: 'bg-success-container text-on-success-container border-success/30',\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-3\">\n <div class=\"flex items-center gap-1\">\n <MIconButton icon=\"chevron_left\" label=\"Anterior\" :size=\"36\" @click=\"navigate(-1)\" />\n <MIconButton icon=\"chevron_right\" label=\"Siguiente\" :size=\"36\" @click=\"navigate(1)\" />\n <button\n type=\"button\"\n class=\"ml-2 cursor-pointer rounded-full border border-outline px-3 py-1 text-label-medium text-on-surface transition-colors hover:bg-on-surface/8\"\n @click=\"goToday\"\n >\n Hoy\n </button>\n </div>\n\n <h3 class=\"text-title-medium font-medium capitalize text-on-surface\">{{ headerLabel }}</h3>\n\n <div class=\"flex rounded-full bg-surface-container-high p-0.5\">\n <button\n v-for=\"v in (['day', 'week'] as const)\"\n :key=\"v\"\n type=\"button\"\n class=\"cursor-pointer rounded-full px-3 py-1 text-label-medium transition-all duration-150\"\n :class=\"currentView === v ? 'bg-secondary-container text-on-secondary-container shadow-elevation-1' : 'text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"currentView = v\"\n >\n {{ v === 'day' ? 'Día' : 'Semana' }}\n </button>\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"overflow-auto\">\n <table class=\"w-full border-collapse\">\n <!-- Day headers -->\n <thead>\n <tr>\n <th class=\"sticky top-0 z-10 w-16 border-b border-r border-outline-variant bg-surface-container p-2\" />\n <th\n v-for=\"day in visibleDays\"\n :key=\"fmt(day)\"\n class=\"sticky top-0 z-10 border-b border-r border-outline-variant bg-surface-container px-2 py-2 text-center last:border-r-0\"\n :class=\"fmt(day) === todayIso ? 'bg-primary-container/30' : ''\"\n >\n <div class=\"text-label-small uppercase text-on-surface-variant\">{{ dayFormat.format(day) }}</div>\n <div\n class=\"mx-auto mt-0.5 flex h-8 w-8 items-center justify-center rounded-full text-title-medium\"\n :class=\"fmt(day) === todayIso ? 'bg-primary text-on-primary font-medium' : 'text-on-surface'\"\n >\n {{ dateFormat.format(day) }}\n </div>\n </th>\n </tr>\n </thead>\n\n <tbody>\n <tr v-for=\"hour in hours\" :key=\"hour\">\n <!-- Hour label -->\n <td class=\"w-16 border-r border-b border-outline-variant/50 p-0 pr-2 text-right align-top\">\n <span class=\"relative -top-2.5 text-label-small text-on-surface-variant\">\n {{ String(hour).padStart(2, '0') }}:00\n </span>\n </td>\n\n <!-- Day cells -->\n <td\n v-for=\"day in visibleDays\"\n :key=\"fmt(day)\"\n class=\"relative h-14 border-r border-b border-outline-variant/50 p-0 last:border-r-0\"\n :class=\"fmt(day) === todayIso ? 'bg-primary-container/[0.05]' : ''\"\n @click=\"emit('slotClick', { date: fmt(day), hour })\"\n >\n <template v-for=\"ev in eventsForDayHour(day, hour)\" :key=\"ev.id\">\n <button\n v-if=\"isEventStart(ev, hour)\"\n type=\"button\"\n class=\"absolute inset-x-0.5 top-0.5 z-[5] cursor-pointer overflow-hidden rounded border-l-[3px] px-2 py-1 text-left transition-opacity hover:opacity-90\"\n :class=\"eventColors[ev.color ?? 'primary']\"\n :style=\"{ height: `calc(${eventDuration(ev) * 100}% - 4px)` }\"\n @click.stop=\"emit('eventClick', ev)\"\n >\n <p class=\"truncate text-label-small font-medium\">{{ ev.title }}</p>\n <p class=\"truncate text-label-small opacity-70\">{{ timeLabel(ev) }}</p>\n </button>\n </template>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nexport interface SegmentedOption {\n value: string | number\n label: string\n icon?: string\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number | (string | number)[]\n options: SegmentedOption[]\n multiSelect?: boolean\n density?: 'default' | 'comfortable' | 'compact'\n color?: 'primary' | 'secondary' | 'tertiary'\n}>(), { multiSelect: false, density: 'default', color: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number | (string | number)[]] }>()\n\nfunction isSelected(value: string | number, modelValue: string | number | (string | number)[]) {\n return Array.isArray(modelValue) ? modelValue.includes(value) : modelValue === value\n}\n\nfunction toggle(opt: SegmentedOption, modelValue: string | number | (string | number)[], multi: boolean) {\n if (opt.disabled) return\n if (multi) {\n const arr = Array.isArray(modelValue) ? [...modelValue] : [modelValue]\n const idx = arr.indexOf(opt.value)\n if (idx >= 0) arr.splice(idx, 1)\n else arr.push(opt.value)\n emit('update:modelValue', arr)\n } else {\n emit('update:modelValue', opt.value)\n }\n}\n</script>\n\n<template>\n <div\n class=\"inline-flex overflow-hidden rounded-full border border-outline\"\n role=\"group\"\n >\n <button\n v-for=\"(opt, i) in options\"\n :key=\"opt.value\"\n type=\"button\"\n class=\"relative inline-flex items-center justify-center gap-2 text-label-large font-medium transition-[background-color,color] duration-150 outline-none before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 enabled:hover:before:opacity-[0.08] enabled:active:before:opacity-[0.12]\"\n :class=\"[\n density === 'compact' ? 'h-8 px-3' : density === 'comfortable' ? 'h-10 px-4' : 'h-10 px-6',\n i > 0 ? 'border-l border-outline' : '',\n opt.disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer',\n isSelected(opt.value, modelValue)\n ? color === 'secondary'\n ? 'bg-secondary-container text-on-secondary-container'\n : color === 'tertiary'\n ? 'bg-tertiary-container text-on-tertiary-container'\n : 'bg-secondary-container text-on-secondary-container'\n : 'text-on-surface',\n ]\"\n :disabled=\"opt.disabled\"\n :aria-pressed=\"isSelected(opt.value, modelValue)\"\n @click=\"toggle(opt, modelValue, multiSelect)\"\n >\n <MIcon\n v-if=\"isSelected(opt.value, modelValue)\"\n name=\"check\"\n :size=\"18\"\n class=\"transition-transform duration-150\"\n />\n <MIcon v-else-if=\"opt.icon\" :name=\"opt.icon\" :size=\"18\" />\n <span>{{ opt.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nexport interface SegmentedOption {\n value: string | number\n label: string\n icon?: string\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number | (string | number)[]\n options: SegmentedOption[]\n multiSelect?: boolean\n density?: 'default' | 'comfortable' | 'compact'\n color?: 'primary' | 'secondary' | 'tertiary'\n}>(), { multiSelect: false, density: 'default', color: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number | (string | number)[]] }>()\n\nfunction isSelected(value: string | number, modelValue: string | number | (string | number)[]) {\n return Array.isArray(modelValue) ? modelValue.includes(value) : modelValue === value\n}\n\nfunction toggle(opt: SegmentedOption, modelValue: string | number | (string | number)[], multi: boolean) {\n if (opt.disabled) return\n if (multi) {\n const arr = Array.isArray(modelValue) ? [...modelValue] : [modelValue]\n const idx = arr.indexOf(opt.value)\n if (idx >= 0) arr.splice(idx, 1)\n else arr.push(opt.value)\n emit('update:modelValue', arr)\n } else {\n emit('update:modelValue', opt.value)\n }\n}\n</script>\n\n<template>\n <div\n class=\"inline-flex overflow-hidden rounded-full border border-outline\"\n role=\"group\"\n >\n <button\n v-for=\"(opt, i) in options\"\n :key=\"opt.value\"\n type=\"button\"\n class=\"relative inline-flex items-center justify-center gap-2 text-label-large font-medium transition-[background-color,color] duration-150 outline-none before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 enabled:hover:before:opacity-[0.08] enabled:active:before:opacity-[0.12]\"\n :class=\"[\n density === 'compact' ? 'h-8 px-3' : density === 'comfortable' ? 'h-10 px-4' : 'h-10 px-6',\n i > 0 ? 'border-l border-outline' : '',\n opt.disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer',\n isSelected(opt.value, modelValue)\n ? color === 'secondary'\n ? 'bg-secondary-container text-on-secondary-container'\n : color === 'tertiary'\n ? 'bg-tertiary-container text-on-tertiary-container'\n : 'bg-secondary-container text-on-secondary-container'\n : 'text-on-surface',\n ]\"\n :disabled=\"opt.disabled\"\n :aria-pressed=\"isSelected(opt.value, modelValue)\"\n @click=\"toggle(opt, modelValue, multiSelect)\"\n >\n <MIcon\n v-if=\"isSelected(opt.value, modelValue)\"\n name=\"check\"\n :size=\"18\"\n class=\"transition-transform duration-150\"\n />\n <MIcon v-else-if=\"opt.icon\" :name=\"opt.icon\" :size=\"18\" />\n <span>{{ opt.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, onMounted, onUnmounted } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number | null\n options: { label: string; value: string | number; disabled?: boolean }[]\n label?: string\n placeholder?: string\n variant?: 'filled' | 'outlined'\n disabled?: boolean\n error?: string\n hint?: string\n required?: boolean\n leadingIcon?: string\n fieldBg?: string\n }>(),\n {\n modelValue: null,\n variant: 'filled',\n disabled: false,\n required: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number] }>()\n\nconst id = useId()\nconst open = ref(false)\nconst fieldEl = ref<HTMLElement | null>(null)\nconst { resolvedFieldBg } = useFieldBg(fieldEl, () => props.fieldBg)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px', width: '0px' })\n\nconst hasValue = computed(() => props.modelValue !== null && props.modelValue !== '')\nconst selectedLabel = computed(\n () => props.options.find((o) => o.value === props.modelValue)?.label ?? '',\n)\n\nfunction computeDropPos() {\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const dropH = Math.min(240, props.options.length * 52 + 8)\n const openAbove = spaceBelow < dropH && rect.top > dropH\n dropPos.value = {\n top: openAbove ? `${rect.top - 4 - dropH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n }\n}\n\nfunction toggle() {\n if (props.disabled) return\n if (!open.value) computeDropPos()\n open.value = !open.value\n}\n\nfunction select(opt: { value: string | number; disabled?: boolean }) {\n if (opt.disabled) return\n emit('update:modelValue', opt.value)\n open.value = false\n}\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!fieldEl.value?.contains(t) && !dropdownEl.value?.contains(t)) open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n // Scrolling inside the dropdown list itself — do nothing\n if (dropdownEl.value?.contains(e.target as Node)) return\n // Recompute position to track the trigger element as the page scrolls\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n // Only close if the trigger has scrolled completely out of the viewport\n if (rect.bottom < 0 || rect.top > window.innerHeight) {\n open.value = false\n return\n }\n computeDropPos()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') { open.value = false; return }\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); return }\n if (!open.value) return\n const opts = props.options.filter((o) => !o.disabled)\n const idx = opts.findIndex((o) => o.value === props.modelValue)\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = opts[(idx + 1) % opts.length]\n if (next) emit('update:modelValue', next.value)\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n const prev = opts[(idx - 1 + opts.length) % opts.length]\n if (prev) emit('update:modelValue', prev.value)\n }\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst triggerClasses = computed(() => {\n const pl = props.leadingIcon ? 'pl-12' : 'pl-4'\n const base = [\n 'flex w-full cursor-pointer items-center pr-10 text-body-large transition-[border-color,border-width] duration-150',\n pl,\n ]\n\n if (props.variant === 'outlined') {\n return [\n ...base,\n 'h-14 rounded-sm border bg-transparent',\n open.value\n ? (props.error ? 'border-2 border-error' : 'border-2 border-primary')\n : (props.error ? 'border-error' : 'border-outline hover:border-on-surface'),\n ].join(' ')\n }\n\n return [\n ...base,\n 'h-14 rounded-t-sm bg-surface-container-highest border-b pt-6 pb-2',\n open.value\n ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')\n : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),\n ].join(' ')\n})\n\nconst isFloated = computed(() => hasValue.value || open.value)\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? (props.variant === 'outlined' ? 'left-11' : 'left-12')\n : (props.variant === 'outlined' ? 'left-3' : 'left-4')\n\n const floated = props.variant === 'outlined'\n ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'\n : 'top-2 translate-y-0 text-label-small'\n\n const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'\n\n return [\n 'pointer-events-none absolute right-10 truncate transition-all duration-200',\n left,\n isFloated.value ? floated : unFloated,\n open.value\n ? (props.error ? 'text-error' : 'text-primary')\n : (props.error ? 'text-error' : 'text-on-surface-variant'),\n ].join(' ')\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div\n ref=\"fieldEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <!-- Leading icon -->\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <!-- Custom trigger -->\n <div\n :id=\"id\"\n :tabindex=\"disabled ? -1 : 0\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled\"\n :class=\"[triggerClasses, disabled ? 'pointer-events-none opacity-[0.38]' : '']\"\n @click=\"toggle\"\n @keydown=\"onKeydown\"\n >\n <span v-if=\"hasValue\" class=\"text-on-surface\">{{ selectedLabel }}</span>\n </div>\n\n <!-- Floating label -->\n <label :for=\"id\" :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <!-- Arrow icon -->\n <div class=\"pointer-events-none absolute right-2 top-1/2 -translate-y-1/2\">\n <MIcon\n :name=\"open ? 'arrow_drop_up' : 'arrow_drop_down'\"\n :size=\"24\"\n class=\"text-on-surface-variant transition-transform duration-200\"\n />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n\n <!-- Dropdown teleported to body to escape overflow clipping -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n enter-to-class=\"opacity-100 translate-y-0 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-from-class=\"opacity-100 translate-y-0 scale-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] max-h-60 overflow-auto rounded-sm bg-surface-container py-1 shadow-elevation-2\"\n :style=\"dropPos\"\n >\n <div\n v-for=\"opt in options\"\n :key=\"opt.value\"\n class=\"flex cursor-pointer items-center gap-3 px-4 py-3 text-body-large\"\n :class=\"[\n opt.disabled\n ? 'cursor-not-allowed opacity-38 text-on-surface'\n : 'text-on-surface hover:bg-on-surface/8',\n opt.value === modelValue ? 'bg-primary/8 text-primary font-medium' : '',\n ]\"\n @click=\"select(opt)\"\n >\n <MIcon\n v-if=\"opt.value === modelValue\"\n name=\"check\"\n :size=\"18\"\n class=\"shrink-0 text-primary\"\n />\n <span v-else class=\"w-[18px] shrink-0\" />\n {{ opt.label }}\n </div>\n <p\n v-if=\"!options.length\"\n class=\"px-4 py-3 text-center text-body-small text-on-surface-variant\"\n >\n Sin opciones\n </p>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, onMounted, onUnmounted } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number | null\n options: { label: string; value: string | number; disabled?: boolean }[]\n label?: string\n placeholder?: string\n variant?: 'filled' | 'outlined'\n disabled?: boolean\n error?: string\n hint?: string\n required?: boolean\n leadingIcon?: string\n fieldBg?: string\n }>(),\n {\n modelValue: null,\n variant: 'filled',\n disabled: false,\n required: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number] }>()\n\nconst id = useId()\nconst open = ref(false)\nconst fieldEl = ref<HTMLElement | null>(null)\nconst { resolvedFieldBg } = useFieldBg(fieldEl, () => props.fieldBg)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px', width: '0px' })\n\nconst hasValue = computed(() => props.modelValue !== null && props.modelValue !== '')\nconst selectedLabel = computed(\n () => props.options.find((o) => o.value === props.modelValue)?.label ?? '',\n)\n\nfunction computeDropPos() {\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const dropH = Math.min(240, props.options.length * 52 + 8)\n const openAbove = spaceBelow < dropH && rect.top > dropH\n dropPos.value = {\n top: openAbove ? `${rect.top - 4 - dropH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n }\n}\n\nfunction toggle() {\n if (props.disabled) return\n if (!open.value) computeDropPos()\n open.value = !open.value\n}\n\nfunction select(opt: { value: string | number; disabled?: boolean }) {\n if (opt.disabled) return\n emit('update:modelValue', opt.value)\n open.value = false\n}\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!fieldEl.value?.contains(t) && !dropdownEl.value?.contains(t)) open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n // Scrolling inside the dropdown list itself — do nothing\n if (dropdownEl.value?.contains(e.target as Node)) return\n // Recompute position to track the trigger element as the page scrolls\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n // Only close if the trigger has scrolled completely out of the viewport\n if (rect.bottom < 0 || rect.top > window.innerHeight) {\n open.value = false\n return\n }\n computeDropPos()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') { open.value = false; return }\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); return }\n if (!open.value) return\n const opts = props.options.filter((o) => !o.disabled)\n const idx = opts.findIndex((o) => o.value === props.modelValue)\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = opts[(idx + 1) % opts.length]\n if (next) emit('update:modelValue', next.value)\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n const prev = opts[(idx - 1 + opts.length) % opts.length]\n if (prev) emit('update:modelValue', prev.value)\n }\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst triggerClasses = computed(() => {\n const pl = props.leadingIcon ? 'pl-12' : 'pl-4'\n const base = [\n 'flex w-full cursor-pointer items-center pr-10 text-body-large transition-[border-color,border-width] duration-150',\n pl,\n ]\n\n if (props.variant === 'outlined') {\n return [\n ...base,\n 'h-14 rounded-sm border bg-transparent',\n open.value\n ? (props.error ? 'border-2 border-error' : 'border-2 border-primary')\n : (props.error ? 'border-error' : 'border-outline hover:border-on-surface'),\n ].join(' ')\n }\n\n return [\n ...base,\n 'h-14 rounded-t-sm bg-surface-container-highest border-b pt-6 pb-2',\n open.value\n ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')\n : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),\n ].join(' ')\n})\n\nconst isFloated = computed(() => hasValue.value || open.value)\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? (props.variant === 'outlined' ? 'left-11' : 'left-12')\n : (props.variant === 'outlined' ? 'left-3' : 'left-4')\n\n const floated = props.variant === 'outlined'\n ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'\n : 'top-2 translate-y-0 text-label-small'\n\n const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'\n\n return [\n 'pointer-events-none absolute right-10 truncate transition-all duration-200',\n left,\n isFloated.value ? floated : unFloated,\n open.value\n ? (props.error ? 'text-error' : 'text-primary')\n : (props.error ? 'text-error' : 'text-on-surface-variant'),\n ].join(' ')\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div\n ref=\"fieldEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <!-- Leading icon -->\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <!-- Custom trigger -->\n <div\n :id=\"id\"\n :tabindex=\"disabled ? -1 : 0\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled\"\n :class=\"[triggerClasses, disabled ? 'pointer-events-none opacity-[0.38]' : '']\"\n @click=\"toggle\"\n @keydown=\"onKeydown\"\n >\n <span v-if=\"hasValue\" class=\"text-on-surface\">{{ selectedLabel }}</span>\n </div>\n\n <!-- Floating label -->\n <label :for=\"id\" :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <!-- Arrow icon -->\n <div class=\"pointer-events-none absolute right-2 top-1/2 -translate-y-1/2\">\n <MIcon\n :name=\"open ? 'arrow_drop_up' : 'arrow_drop_down'\"\n :size=\"24\"\n class=\"text-on-surface-variant transition-transform duration-200\"\n />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n\n <!-- Dropdown teleported to body to escape overflow clipping -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n enter-to-class=\"opacity-100 translate-y-0 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-from-class=\"opacity-100 translate-y-0 scale-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] max-h-60 overflow-auto rounded-sm bg-surface-container py-1 shadow-elevation-2\"\n :style=\"dropPos\"\n >\n <div\n v-for=\"opt in options\"\n :key=\"opt.value\"\n class=\"flex cursor-pointer items-center gap-3 px-4 py-3 text-body-large\"\n :class=\"[\n opt.disabled\n ? 'cursor-not-allowed opacity-38 text-on-surface'\n : 'text-on-surface hover:bg-on-surface/8',\n opt.value === modelValue ? 'bg-primary/8 text-primary font-medium' : '',\n ]\"\n @click=\"select(opt)\"\n >\n <MIcon\n v-if=\"opt.value === modelValue\"\n name=\"check\"\n :size=\"18\"\n class=\"shrink-0 text-primary\"\n />\n <span v-else class=\"w-[18px] shrink-0\" />\n {{ opt.label }}\n </div>\n <p\n v-if=\"!options.length\"\n class=\"px-4 py-3 text-center text-body-small text-on-surface-variant\"\n >\n Sin opciones\n </p>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n modelValue: boolean\n title?: string\n width?: string\n}>(), { width: 'w-80' })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\nconst close = () => emit('update:modelValue', false)\n\nconst dragX = ref(0)\nconst dragging = ref(false)\nlet startX = 0\n\nfunction onEdgePointerDown(e: PointerEvent) {\n dragging.value = true\n startX = e.clientX\n dragX.value = 0\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onEdgePointerMove(e: PointerEvent) {\n if (!dragging.value) return\n dragX.value = Math.max(0, e.clientX - startX)\n}\nfunction onEdgePointerUp() {\n if (dragX.value > 100) close()\n dragging.value = false\n dragX.value = 0\n}\n\nconst panelStyle = computed(() => ({\n transform: `translateX(${dragX.value}px)`,\n transition: dragging.value ? 'none' : undefined,\n}))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"ss\" :duration=\"{ enter: 320, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[200] flex justify-end\">\n <!-- Scrim -->\n <div class=\"ss-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <aside\n class=\"ss-panel relative flex h-full flex-col bg-surface-container-low shadow-elevation-3\"\n :class=\"[width, 'max-w-[90vw]']\"\n :style=\"panelStyle\"\n >\n <!-- Drag edge -->\n <div\n class=\"absolute top-0 left-0 h-full w-3 cursor-ew-resize touch-none\"\n @pointerdown=\"onEdgePointerDown\"\n @pointermove=\"onEdgePointerMove\"\n @pointerup=\"onEdgePointerUp\"\n />\n\n <!-- Header -->\n <div v-if=\"title || $slots.header\" class=\"flex shrink-0 items-center justify-between border-b border-outline-variant px-6 py-4\">\n <slot name=\"header\">\n <h2 class=\"text-title-large text-on-surface\">{{ title }}</h2>\n </slot>\n <button\n type=\"button\"\n class=\"flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"20\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"flex-1 overflow-y-auto px-6 py-4\">\n <slot />\n </div>\n\n <!-- Actions -->\n <div v-if=\"$slots.actions\" class=\"shrink-0 border-t border-outline-variant px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </aside>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.ss-scrim {\n transition: opacity 280ms ease;\n}\n.ss-enter-from .ss-scrim,\n.ss-leave-to .ss-scrim {\n opacity: 0;\n}\n\n.ss-panel {\n transition:\n transform 320ms cubic-bezier(0.2, 0, 0, 1),\n opacity 240ms ease;\n}\n.ss-enter-from .ss-panel {\n transform: translateX(40%);\n opacity: 0;\n}\n.ss-leave-to .ss-panel {\n transform: translateX(100%) !important;\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n modelValue: boolean\n title?: string\n width?: string\n}>(), { width: 'w-80' })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\nconst close = () => emit('update:modelValue', false)\n\nconst dragX = ref(0)\nconst dragging = ref(false)\nlet startX = 0\n\nfunction onEdgePointerDown(e: PointerEvent) {\n dragging.value = true\n startX = e.clientX\n dragX.value = 0\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onEdgePointerMove(e: PointerEvent) {\n if (!dragging.value) return\n dragX.value = Math.max(0, e.clientX - startX)\n}\nfunction onEdgePointerUp() {\n if (dragX.value > 100) close()\n dragging.value = false\n dragX.value = 0\n}\n\nconst panelStyle = computed(() => ({\n transform: `translateX(${dragX.value}px)`,\n transition: dragging.value ? 'none' : undefined,\n}))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"ss\" :duration=\"{ enter: 320, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[200] flex justify-end\">\n <!-- Scrim -->\n <div class=\"ss-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <aside\n class=\"ss-panel relative flex h-full flex-col bg-surface-container-low shadow-elevation-3\"\n :class=\"[width, 'max-w-[90vw]']\"\n :style=\"panelStyle\"\n >\n <!-- Drag edge -->\n <div\n class=\"absolute top-0 left-0 h-full w-3 cursor-ew-resize touch-none\"\n @pointerdown=\"onEdgePointerDown\"\n @pointermove=\"onEdgePointerMove\"\n @pointerup=\"onEdgePointerUp\"\n />\n\n <!-- Header -->\n <div v-if=\"title || $slots.header\" class=\"flex shrink-0 items-center justify-between border-b border-outline-variant px-6 py-4\">\n <slot name=\"header\">\n <h2 class=\"text-title-large text-on-surface\">{{ title }}</h2>\n </slot>\n <button\n type=\"button\"\n class=\"flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"20\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"flex-1 overflow-y-auto px-6 py-4\">\n <slot />\n </div>\n\n <!-- Actions -->\n <div v-if=\"$slots.actions\" class=\"shrink-0 border-t border-outline-variant px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </aside>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.ss-scrim {\n transition: opacity 280ms ease;\n}\n.ss-enter-from .ss-scrim,\n.ss-leave-to .ss-scrim {\n opacity: 0;\n}\n\n.ss-panel {\n transition:\n transform 320ms cubic-bezier(0.2, 0, 0, 1),\n opacity 240ms ease;\n}\n.ss-enter-from .ss-panel {\n transform: translateX(40%);\n opacity: 0;\n}\n.ss-leave-to .ss-panel {\n transform: translateX(100%) !important;\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n variant?: 'text' | 'circular' | 'rectangular' | 'rounded'\n width?: string\n height?: string\n lines?: number\n animation?: 'pulse' | 'wave' | 'none'\n}>(), {\n variant: 'text',\n animation: 'pulse',\n lines: 1,\n})\n</script>\n\n<template>\n <!-- Multi-line text skeleton -->\n <div v-if=\"variant === 'text' && lines > 1\" class=\"flex flex-col gap-2.5\">\n <div\n v-for=\"i in lines\"\n :key=\"i\"\n class=\"h-3.5 rounded-full bg-on-surface/10\"\n :class=\"animation === 'pulse' ? 'animate-pulse' : animation === 'wave' ? 'skeleton-wave' : ''\"\n :style=\"{ width: i === lines ? '60%' : (width ?? '100%') }\"\n />\n </div>\n\n <!-- Single element -->\n <div\n v-else\n class=\"bg-on-surface/10\"\n :class=\"[\n variant === 'circular' ? 'rounded-full' : variant === 'rounded' ? 'rounded-lg' : variant === 'text' ? 'rounded-full' : 'rounded-sm',\n animation === 'pulse' ? 'animate-pulse' : animation === 'wave' ? 'skeleton-wave' : '',\n ]\"\n :style=\"{\n width: width ?? (variant === 'circular' ? '40px' : '100%'),\n height: height ?? (variant === 'circular' ? '40px' : variant === 'text' ? '14px' : '100px'),\n }\"\n />\n</template>\n\n<style scoped>\n@keyframes skeleton-wave-move {\n 0% { transform: translateX(-100%); }\n 60% { transform: translateX(100%); }\n 100% { transform: translateX(100%); }\n}\n.skeleton-wave {\n position: relative;\n overflow: hidden;\n}\n.skeleton-wave::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent 0%, var(--color-on-surface) 50%, transparent 100%);\n opacity: 0.06;\n animation: skeleton-wave-move 1.8s ease-in-out infinite;\n}\n</style>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n variant?: 'text' | 'circular' | 'rectangular' | 'rounded'\n width?: string\n height?: string\n lines?: number\n animation?: 'pulse' | 'wave' | 'none'\n}>(), {\n variant: 'text',\n animation: 'pulse',\n lines: 1,\n})\n</script>\n\n<template>\n <!-- Multi-line text skeleton -->\n <div v-if=\"variant === 'text' && lines > 1\" class=\"flex flex-col gap-2.5\">\n <div\n v-for=\"i in lines\"\n :key=\"i\"\n class=\"h-3.5 rounded-full bg-on-surface/10\"\n :class=\"animation === 'pulse' ? 'animate-pulse' : animation === 'wave' ? 'skeleton-wave' : ''\"\n :style=\"{ width: i === lines ? '60%' : (width ?? '100%') }\"\n />\n </div>\n\n <!-- Single element -->\n <div\n v-else\n class=\"bg-on-surface/10\"\n :class=\"[\n variant === 'circular' ? 'rounded-full' : variant === 'rounded' ? 'rounded-lg' : variant === 'text' ? 'rounded-full' : 'rounded-sm',\n animation === 'pulse' ? 'animate-pulse' : animation === 'wave' ? 'skeleton-wave' : '',\n ]\"\n :style=\"{\n width: width ?? (variant === 'circular' ? '40px' : '100%'),\n height: height ?? (variant === 'circular' ? '40px' : variant === 'text' ? '14px' : '100px'),\n }\"\n />\n</template>\n\n<style scoped>\n@keyframes skeleton-wave-move {\n 0% { transform: translateX(-100%); }\n 60% { transform: translateX(100%); }\n 100% { transform: translateX(100%); }\n}\n.skeleton-wave {\n position: relative;\n overflow: hidden;\n}\n.skeleton-wave::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent 0%, var(--color-on-surface) 50%, transparent 100%);\n opacity: 0.06;\n animation: skeleton-wave-move 1.8s ease-in-out infinite;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n step?: number;\n disabled?: boolean;\n label?: string;\n showValue?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n }>(),\n {\n min: 0,\n max: 100,\n step: 1,\n disabled: false,\n showValue: false,\n color: \"primary\",\n },\n);\n\nconst emit = defineEmits<{\n \"update:modelValue\": [number];\n}>();\n\nconst trackEl = ref<HTMLElement>();\nconst dragging = ref(false);\n\nconst pct = computed(() => {\n const range = props.max - props.min;\n return range === 0 ? 0 : ((props.modelValue - props.min) / range) * 100;\n});\n\nconst colors: Record<string, { active: string; inactive: string; thumb: string }> = {\n primary: {\n active: \"bg-primary\",\n inactive: \"bg-primary-container\",\n thumb: \"bg-primary\",\n },\n secondary: {\n active: \"bg-secondary\",\n inactive: \"bg-secondary-container\",\n thumb: \"bg-secondary\",\n },\n tertiary: {\n active: \"bg-tertiary\",\n inactive: \"bg-tertiary-container\",\n thumb: \"bg-tertiary\",\n },\n error: {\n active: \"bg-error\",\n inactive: \"bg-error-container\",\n thumb: \"bg-error\",\n },\n};\n\nfunction clamp(v: number) {\n const stepped = Math.round((v - props.min) / props.step) * props.step + props.min;\n\n return Math.max(props.min, Math.min(props.max, stepped));\n}\n\nfunction valueFromX(clientX: number) {\n if (!trackEl.value) return props.modelValue;\n\n const rect = trackEl.value.getBoundingClientRect();\n\n const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n\n return clamp(props.min + ratio * (props.max - props.min));\n}\n\nfunction onPointerDown(e: PointerEvent) {\n if (props.disabled) return;\n\n e.preventDefault();\n\n dragging.value = true;\n\n emit(\"update:modelValue\", valueFromX(e.clientX));\n\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerUp);\n}\n\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value) return;\n\n emit(\"update:modelValue\", valueFromX(e.clientX));\n}\n\nfunction onPointerUp() {\n dragging.value = false;\n\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n}\n\nfunction onKeyDown(e: KeyboardEvent) {\n if (props.disabled) return;\n\n const delta = {\n ArrowRight: 1,\n ArrowUp: 1,\n ArrowLeft: -1,\n ArrowDown: -1,\n }[e.key];\n\n if (delta !== undefined) {\n e.preventDefault();\n\n emit(\"update:modelValue\", clamp(props.modelValue + delta * props.step));\n }\n\n if (e.key === \"Home\") {\n e.preventDefault();\n emit(\"update:modelValue\", props.min);\n }\n\n if (e.key === \"End\") {\n e.preventDefault();\n emit(\"update:modelValue\", props.max);\n }\n}\n\nonBeforeUnmount(() => {\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n});\n\nconst thumbStyle = computed(() => ({\n left: `${pct.value}%`,\n top: \"50%\",\n transform: `translateX(-50%) translateY(-50%) scale(${dragging.value ? 1.15 : 1})`,\n transition: dragging.value ? \"transform 80ms ease\" : \"left 75ms ease, transform 80ms ease\",\n}));\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1 select-none\">\n <div v-if=\"label || showValue\" class=\"flex items-center justify-between\">\n <span v-if=\"label\" class=\"text-label-large text-on-surface\">\n {{ label }}\n </span>\n\n <span v-if=\"showValue\" class=\"tabular-nums text-label-large text-on-surface-variant\">\n {{ modelValue }}\n </span>\n </div>\n\n <div\n ref=\"trackEl\"\n role=\"slider\"\n tabindex=\"0\"\n :aria-valuenow=\"modelValue\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-disabled=\"disabled || undefined\"\n class=\"relative flex h-10 w-full touch-none cursor-pointer items-center outline-none\"\n :class=\"disabled && 'cursor-not-allowed opacity-[0.38]'\"\n @pointerdown=\"onPointerDown\"\n @keydown=\"onKeyDown\"\n >\n <div class=\"relative h-1 w-full rounded-full\" :class=\"colors[color]!.inactive\">\n <div\n class=\"h-full rounded-full\"\n :class=\"colors[color]!.active\"\n :style=\"{\n width: `${pct}%`,\n transition: dragging ? 'none' : 'width 75ms ease',\n }\"\n />\n </div>\n\n <div\n class=\"pointer-events-none absolute h-5 w-5 rounded-full shadow-elevation-1\"\n :class=\"colors[color]!.thumb\"\n :style=\"thumbStyle\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n step?: number;\n disabled?: boolean;\n label?: string;\n showValue?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n }>(),\n {\n min: 0,\n max: 100,\n step: 1,\n disabled: false,\n showValue: false,\n color: \"primary\",\n },\n);\n\nconst emit = defineEmits<{\n \"update:modelValue\": [number];\n}>();\n\nconst trackEl = ref<HTMLElement>();\nconst dragging = ref(false);\n\nconst pct = computed(() => {\n const range = props.max - props.min;\n return range === 0 ? 0 : ((props.modelValue - props.min) / range) * 100;\n});\n\nconst colors: Record<string, { active: string; inactive: string; thumb: string }> = {\n primary: {\n active: \"bg-primary\",\n inactive: \"bg-primary-container\",\n thumb: \"bg-primary\",\n },\n secondary: {\n active: \"bg-secondary\",\n inactive: \"bg-secondary-container\",\n thumb: \"bg-secondary\",\n },\n tertiary: {\n active: \"bg-tertiary\",\n inactive: \"bg-tertiary-container\",\n thumb: \"bg-tertiary\",\n },\n error: {\n active: \"bg-error\",\n inactive: \"bg-error-container\",\n thumb: \"bg-error\",\n },\n};\n\nfunction clamp(v: number) {\n const stepped = Math.round((v - props.min) / props.step) * props.step + props.min;\n\n return Math.max(props.min, Math.min(props.max, stepped));\n}\n\nfunction valueFromX(clientX: number) {\n if (!trackEl.value) return props.modelValue;\n\n const rect = trackEl.value.getBoundingClientRect();\n\n const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n\n return clamp(props.min + ratio * (props.max - props.min));\n}\n\nfunction onPointerDown(e: PointerEvent) {\n if (props.disabled) return;\n\n e.preventDefault();\n\n dragging.value = true;\n\n emit(\"update:modelValue\", valueFromX(e.clientX));\n\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerUp);\n}\n\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value) return;\n\n emit(\"update:modelValue\", valueFromX(e.clientX));\n}\n\nfunction onPointerUp() {\n dragging.value = false;\n\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n}\n\nfunction onKeyDown(e: KeyboardEvent) {\n if (props.disabled) return;\n\n const delta = {\n ArrowRight: 1,\n ArrowUp: 1,\n ArrowLeft: -1,\n ArrowDown: -1,\n }[e.key];\n\n if (delta !== undefined) {\n e.preventDefault();\n\n emit(\"update:modelValue\", clamp(props.modelValue + delta * props.step));\n }\n\n if (e.key === \"Home\") {\n e.preventDefault();\n emit(\"update:modelValue\", props.min);\n }\n\n if (e.key === \"End\") {\n e.preventDefault();\n emit(\"update:modelValue\", props.max);\n }\n}\n\nonBeforeUnmount(() => {\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n});\n\nconst thumbStyle = computed(() => ({\n left: `${pct.value}%`,\n top: \"50%\",\n transform: `translateX(-50%) translateY(-50%) scale(${dragging.value ? 1.15 : 1})`,\n transition: dragging.value ? \"transform 80ms ease\" : \"left 75ms ease, transform 80ms ease\",\n}));\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1 select-none\">\n <div v-if=\"label || showValue\" class=\"flex items-center justify-between\">\n <span v-if=\"label\" class=\"text-label-large text-on-surface\">\n {{ label }}\n </span>\n\n <span v-if=\"showValue\" class=\"tabular-nums text-label-large text-on-surface-variant\">\n {{ modelValue }}\n </span>\n </div>\n\n <div\n ref=\"trackEl\"\n role=\"slider\"\n tabindex=\"0\"\n :aria-valuenow=\"modelValue\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-disabled=\"disabled || undefined\"\n class=\"relative flex h-10 w-full touch-none cursor-pointer items-center outline-none\"\n :class=\"disabled && 'cursor-not-allowed opacity-[0.38]'\"\n @pointerdown=\"onPointerDown\"\n @keydown=\"onKeyDown\"\n >\n <div class=\"relative h-1 w-full rounded-full\" :class=\"colors[color]!.inactive\">\n <div\n class=\"h-full rounded-full\"\n :class=\"colors[color]!.active\"\n :style=\"{\n width: `${pct}%`,\n transition: dragging ? 'none' : 'width 75ms ease',\n }\"\n />\n </div>\n\n <div\n class=\"pointer-events-none absolute h-5 w-5 rounded-full shadow-elevation-1\"\n :class=\"colors[color]!.thumb\"\n :style=\"thumbStyle\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useToast } from \"../composables/useToast\";\nimport MIcon from \"./MIcon.vue\";\n\nconst { toasts, position, dismiss } = useToast();\n\nconst isTop = computed(() => position.value.startsWith(\"top\"));\n\nconst containerClass = computed(() => {\n const base = \"pointer-events-none fixed z-[300] flex flex-col\";\n switch (position.value) {\n case \"top-left\":\n return `${base} top-4 left-4 items-start`;\n case \"top-center\":\n return `${base} top-4 left-1/2 -translate-x-1/2 items-center`;\n case \"top-right\":\n return `${base} top-4 right-4 items-end`;\n case \"bottom-left\":\n return `${base} bottom-4 left-4 items-start`;\n case \"bottom-right\":\n return `${base} bottom-4 right-4 items-end`;\n default:\n return `${base} bottom-4 left-1/2 -translate-x-1/2 items-center`;\n }\n});\n\ntype VariantStyle = {\n container: string;\n icon: string;\n iconName: string;\n action: string;\n close: string;\n progress: string;\n};\n\nconst variantStyles: Record<string, VariantStyle> = {\n info: {\n // secondary-container tokens are adaptive light/dark out of the box\n container:\n \"bg-secondary-container text-on-secondary-container ring-1 ring-inset ring-on-secondary-container/8\",\n icon: \"text-on-secondary-container/70\",\n iconName: \"info\",\n action: \"text-on-secondary-container hover:bg-on-secondary-container/10\",\n close: \"text-on-secondary-container/60 hover:bg-on-secondary-container/10\",\n progress: \"bg-on-secondary-container/25\",\n },\n success: {\n container:\n \"bg-[#dcfce7] text-[#14532d] ring-1 ring-inset ring-[#14532d]/10 dark:bg-[#052e16] dark:text-[#bbf7d0] dark:ring-white/8\",\n icon: \"text-[#16a34a] dark:text-[#4ade80]\",\n iconName: \"check_circle\",\n action: \"text-[#166534] hover:bg-[#14532d]/10 dark:text-[#86efac] dark:hover:bg-white/10\",\n close: \"text-[#14532d]/50 hover:bg-[#14532d]/10 dark:text-[#bbf7d0]/50 dark:hover:bg-white/10\",\n progress: \"bg-[#16a34a]/35 dark:bg-[#4ade80]/30\",\n },\n warning: {\n container:\n \"bg-[#fefce8] text-[#713f12] ring-1 ring-inset ring-[#713f12]/10 dark:bg-[#2d1a00] dark:text-[#fde68a] dark:ring-white/8\",\n icon: \"text-[#d97706] dark:text-[#fcd34d]\",\n iconName: \"warning\",\n action: \"text-[#92400e] hover:bg-[#713f12]/10 dark:text-[#fcd34d] dark:hover:bg-white/10\",\n close: \"text-[#713f12]/50 hover:bg-[#713f12]/10 dark:text-[#fde68a]/50 dark:hover:bg-white/10\",\n progress: \"bg-[#d97706]/35 dark:bg-[#fcd34d]/30\",\n },\n error: {\n // error-container tokens are adaptive light/dark out of the box\n container:\n \"bg-error-container text-on-error-container ring-1 ring-inset ring-on-error-container/8\",\n icon: \"text-error dark:text-[#fca5a5]\",\n iconName: \"error\",\n action: \"text-on-error-container hover:bg-on-error-container/10\",\n close: \"text-on-error-container/60 hover:bg-on-error-container/10\",\n progress: \"bg-on-error-container/25\",\n },\n};\n\nconst getVariantStyle = (variant: string): VariantStyle =>\n variantStyles[variant] ?? variantStyles.info!;\n</script>\n\n<template>\n <div :class=\"containerClass\">\n <TransitionGroup :name=\"isTop ? 'm3-toast-top' : 'm3-toast-bot'\">\n <div v-for=\"t in toasts\" :key=\"t.id\" class=\"toast-row w-full min-w-64 max-w-xs\">\n <div\n class=\"toast-inner pointer-events-auto relative flex items-center gap-3 overflow-hidden rounded-2xl px-4 py-4 shadow-elevation-2\"\n :class=\"getVariantStyle(t.variant).container\"\n >\n <MIcon\n :name=\"getVariantStyle(t.variant).iconName\"\n :size=\"20\"\n class=\"shrink-0\"\n :class=\"getVariantStyle(t.variant).icon\"\n />\n\n <p class=\"flex-1 text-body-medium leading-snug\">{{ t.message }}</p>\n\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n v-if=\"t.action\"\n type=\"button\"\n class=\"cursor-pointer rounded px-2 py-1 text-label-medium font-semibold transition-colors\"\n :class=\"getVariantStyle(t.variant).action\"\n @click=\"\n () => {\n t.action!.onClick();\n dismiss(t.id);\n }\n \"\n >\n {{ t.action.label }}\n </button>\n\n <button\n type=\"button\"\n class=\"flex h-8 w-8 cursor-pointer items-center justify-center rounded-full transition-colors\"\n :class=\"getVariantStyle(t.variant).close\"\n aria-label=\"Cerrar\"\n @click=\"dismiss(t.id)\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <!-- Countdown progress bar -->\n <div\n v-if=\"t.duration > 0\"\n class=\"absolute right-0 bottom-0 left-0 h-0.5 origin-left\"\n :class=\"getVariantStyle(t.variant).progress\"\n :style=\"{ animation: `m3-toast-progress ${t.duration}ms linear forwards` }\"\n />\n </div>\n </div>\n </TransitionGroup>\n </div>\n</template>\n\n<style scoped>\n/*\n .toast-row is a grid container — animating grid-template-rows: 1fr → 0fr\n collapses height smoothly without position:absolute, so sibling toasts\n shift up gracefully instead of jumping.\n*/\n.toast-row {\n display: grid;\n grid-template-rows: 1fr;\n padding-bottom: 8px;\n}\n.toast-row > .toast-inner {\n min-height: 0; /* required for 0fr collapse */\n}\n\n/* ─── Bottom toasts ─────────────────────────────────────────────── */\n.m3-toast-bot-enter-active {\n transition:\n grid-template-rows 220ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 220ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-bot-enter-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 220ms cubic-bezier(0.2, 0, 0, 1);\n}\n.m3-toast-bot-enter-from {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-bot-enter-from > .toast-inner {\n opacity: 0;\n transform: translateY(20px) scale(0.94);\n}\n\n.m3-toast-bot-leave-active {\n transition:\n grid-template-rows 300ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 300ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-bot-leave-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 180ms ease;\n}\n.m3-toast-bot-leave-to {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-bot-leave-to > .toast-inner {\n opacity: 0;\n transform: scale(0.92);\n}\n\n/* ─── Top toasts ────────────────────────────────────────────────── */\n.m3-toast-top-enter-active {\n transition:\n grid-template-rows 220ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 220ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-top-enter-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 220ms cubic-bezier(0.2, 0, 0, 1);\n}\n.m3-toast-top-enter-from {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-top-enter-from > .toast-inner {\n opacity: 0;\n transform: translateY(-20px) scale(0.94);\n}\n\n.m3-toast-top-leave-active {\n transition:\n grid-template-rows 300ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 300ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-top-leave-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 180ms ease;\n}\n.m3-toast-top-leave-to {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-top-leave-to > .toast-inner {\n opacity: 0;\n transform: scale(0.92);\n}\n\n@keyframes m3-toast-progress {\n from {\n transform: scaleX(1);\n }\n to {\n transform: scaleX(0);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useToast } from \"../composables/useToast\";\nimport MIcon from \"./MIcon.vue\";\n\nconst { toasts, position, dismiss } = useToast();\n\nconst isTop = computed(() => position.value.startsWith(\"top\"));\n\nconst containerClass = computed(() => {\n const base = \"pointer-events-none fixed z-[300] flex flex-col\";\n switch (position.value) {\n case \"top-left\":\n return `${base} top-4 left-4 items-start`;\n case \"top-center\":\n return `${base} top-4 left-1/2 -translate-x-1/2 items-center`;\n case \"top-right\":\n return `${base} top-4 right-4 items-end`;\n case \"bottom-left\":\n return `${base} bottom-4 left-4 items-start`;\n case \"bottom-right\":\n return `${base} bottom-4 right-4 items-end`;\n default:\n return `${base} bottom-4 left-1/2 -translate-x-1/2 items-center`;\n }\n});\n\ntype VariantStyle = {\n container: string;\n icon: string;\n iconName: string;\n action: string;\n close: string;\n progress: string;\n};\n\nconst variantStyles: Record<string, VariantStyle> = {\n info: {\n // secondary-container tokens are adaptive light/dark out of the box\n container:\n \"bg-secondary-container text-on-secondary-container ring-1 ring-inset ring-on-secondary-container/8\",\n icon: \"text-on-secondary-container/70\",\n iconName: \"info\",\n action: \"text-on-secondary-container hover:bg-on-secondary-container/10\",\n close: \"text-on-secondary-container/60 hover:bg-on-secondary-container/10\",\n progress: \"bg-on-secondary-container/25\",\n },\n success: {\n container:\n \"bg-[#dcfce7] text-[#14532d] ring-1 ring-inset ring-[#14532d]/10 dark:bg-[#052e16] dark:text-[#bbf7d0] dark:ring-white/8\",\n icon: \"text-[#16a34a] dark:text-[#4ade80]\",\n iconName: \"check_circle\",\n action: \"text-[#166534] hover:bg-[#14532d]/10 dark:text-[#86efac] dark:hover:bg-white/10\",\n close: \"text-[#14532d]/50 hover:bg-[#14532d]/10 dark:text-[#bbf7d0]/50 dark:hover:bg-white/10\",\n progress: \"bg-[#16a34a]/35 dark:bg-[#4ade80]/30\",\n },\n warning: {\n container:\n \"bg-[#fefce8] text-[#713f12] ring-1 ring-inset ring-[#713f12]/10 dark:bg-[#2d1a00] dark:text-[#fde68a] dark:ring-white/8\",\n icon: \"text-[#d97706] dark:text-[#fcd34d]\",\n iconName: \"warning\",\n action: \"text-[#92400e] hover:bg-[#713f12]/10 dark:text-[#fcd34d] dark:hover:bg-white/10\",\n close: \"text-[#713f12]/50 hover:bg-[#713f12]/10 dark:text-[#fde68a]/50 dark:hover:bg-white/10\",\n progress: \"bg-[#d97706]/35 dark:bg-[#fcd34d]/30\",\n },\n error: {\n // error-container tokens are adaptive light/dark out of the box\n container:\n \"bg-error-container text-on-error-container ring-1 ring-inset ring-on-error-container/8\",\n icon: \"text-error dark:text-[#fca5a5]\",\n iconName: \"error\",\n action: \"text-on-error-container hover:bg-on-error-container/10\",\n close: \"text-on-error-container/60 hover:bg-on-error-container/10\",\n progress: \"bg-on-error-container/25\",\n },\n};\n\nconst getVariantStyle = (variant: string): VariantStyle =>\n variantStyles[variant] ?? variantStyles.info!;\n</script>\n\n<template>\n <div :class=\"containerClass\">\n <TransitionGroup :name=\"isTop ? 'm3-toast-top' : 'm3-toast-bot'\">\n <div v-for=\"t in toasts\" :key=\"t.id\" class=\"toast-row w-full min-w-64 max-w-xs\">\n <div\n class=\"toast-inner pointer-events-auto relative flex items-center gap-3 overflow-hidden rounded-2xl px-4 py-4 shadow-elevation-2\"\n :class=\"getVariantStyle(t.variant).container\"\n >\n <MIcon\n :name=\"getVariantStyle(t.variant).iconName\"\n :size=\"20\"\n class=\"shrink-0\"\n :class=\"getVariantStyle(t.variant).icon\"\n />\n\n <p class=\"flex-1 text-body-medium leading-snug\">{{ t.message }}</p>\n\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n v-if=\"t.action\"\n type=\"button\"\n class=\"cursor-pointer rounded px-2 py-1 text-label-medium font-semibold transition-colors\"\n :class=\"getVariantStyle(t.variant).action\"\n @click=\"\n () => {\n t.action!.onClick();\n dismiss(t.id);\n }\n \"\n >\n {{ t.action.label }}\n </button>\n\n <button\n type=\"button\"\n class=\"flex h-8 w-8 cursor-pointer items-center justify-center rounded-full transition-colors\"\n :class=\"getVariantStyle(t.variant).close\"\n aria-label=\"Cerrar\"\n @click=\"dismiss(t.id)\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <!-- Countdown progress bar -->\n <div\n v-if=\"t.duration > 0\"\n class=\"absolute right-0 bottom-0 left-0 h-0.5 origin-left\"\n :class=\"getVariantStyle(t.variant).progress\"\n :style=\"{ animation: `m3-toast-progress ${t.duration}ms linear forwards` }\"\n />\n </div>\n </div>\n </TransitionGroup>\n </div>\n</template>\n\n<style scoped>\n/*\n .toast-row is a grid container — animating grid-template-rows: 1fr → 0fr\n collapses height smoothly without position:absolute, so sibling toasts\n shift up gracefully instead of jumping.\n*/\n.toast-row {\n display: grid;\n grid-template-rows: 1fr;\n padding-bottom: 8px;\n}\n.toast-row > .toast-inner {\n min-height: 0; /* required for 0fr collapse */\n}\n\n/* ─── Bottom toasts ─────────────────────────────────────────────── */\n.m3-toast-bot-enter-active {\n transition:\n grid-template-rows 220ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 220ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-bot-enter-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 220ms cubic-bezier(0.2, 0, 0, 1);\n}\n.m3-toast-bot-enter-from {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-bot-enter-from > .toast-inner {\n opacity: 0;\n transform: translateY(20px) scale(0.94);\n}\n\n.m3-toast-bot-leave-active {\n transition:\n grid-template-rows 300ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 300ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-bot-leave-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 180ms ease;\n}\n.m3-toast-bot-leave-to {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-bot-leave-to > .toast-inner {\n opacity: 0;\n transform: scale(0.92);\n}\n\n/* ─── Top toasts ────────────────────────────────────────────────── */\n.m3-toast-top-enter-active {\n transition:\n grid-template-rows 220ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 220ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-top-enter-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 220ms cubic-bezier(0.2, 0, 0, 1);\n}\n.m3-toast-top-enter-from {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-top-enter-from > .toast-inner {\n opacity: 0;\n transform: translateY(-20px) scale(0.94);\n}\n\n.m3-toast-top-leave-active {\n transition:\n grid-template-rows 300ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 300ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-top-leave-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 180ms ease;\n}\n.m3-toast-top-leave-to {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-top-leave-to > .toast-inner {\n opacity: 0;\n transform: scale(0.92);\n}\n\n@keyframes m3-toast-progress {\n from {\n transform: scaleX(1);\n }\n to {\n transform: scaleX(0);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n direction?: 'horizontal' | 'vertical'\n initialSplit?: number\n min?: number\n max?: number\n }>(),\n { direction: 'horizontal', initialSplit: 50, min: 10, max: 90 },\n)\n\nconst split = ref(props.initialSplit)\nconst dragging = ref(false)\nconst containerRef = ref<HTMLElement | null>(null)\n\nconst isHorizontal = computed(() => props.direction === 'horizontal')\n\nconst panelAStyle = computed(() =>\n isHorizontal.value\n ? { width: `${split.value}%` }\n : { height: `${split.value}%` },\n)\n\nconst panelBStyle = computed(() =>\n isHorizontal.value\n ? { width: `${100 - split.value}%` }\n : { height: `${100 - split.value}%` },\n)\n\nfunction onPointerDown(e: PointerEvent) {\n dragging.value = true\n ;(e.target as HTMLElement).setPointerCapture(e.pointerId)\n}\n\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value || !containerRef.value) return\n\n const rect = containerRef.value.getBoundingClientRect()\n let pct: number\n\n if (isHorizontal.value) {\n pct = ((e.clientX - rect.left) / rect.width) * 100\n } else {\n pct = ((e.clientY - rect.top) / rect.height) * 100\n }\n\n split.value = Math.min(props.max, Math.max(props.min, pct))\n}\n\nfunction onPointerUp() {\n dragging.value = false\n}\n\nonBeforeUnmount(() => {\n dragging.value = false\n})\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"flex overflow-hidden\"\n :class=\"[\n isHorizontal ? 'flex-row' : 'flex-col',\n dragging && 'select-none',\n ]\"\n style=\"height: 100%\"\n >\n <div class=\"overflow-auto\" :style=\"panelAStyle\">\n <slot name=\"first\" />\n </div>\n\n <div\n class=\"z-10 flex shrink-0 items-center justify-center transition-colors\"\n :class=\"[\n isHorizontal\n ? 'w-2 cursor-col-resize flex-col'\n : 'h-2 cursor-row-resize flex-row',\n dragging ? 'bg-primary/20' : 'bg-outline-variant/40 hover:bg-primary/12',\n ]\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n >\n <div\n class=\"rounded-full bg-outline\"\n :class=\"isHorizontal ? 'h-6 w-1' : 'h-1 w-6'\"\n />\n </div>\n\n <div class=\"overflow-auto\" :style=\"panelBStyle\">\n <slot name=\"second\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n direction?: 'horizontal' | 'vertical'\n initialSplit?: number\n min?: number\n max?: number\n }>(),\n { direction: 'horizontal', initialSplit: 50, min: 10, max: 90 },\n)\n\nconst split = ref(props.initialSplit)\nconst dragging = ref(false)\nconst containerRef = ref<HTMLElement | null>(null)\n\nconst isHorizontal = computed(() => props.direction === 'horizontal')\n\nconst panelAStyle = computed(() =>\n isHorizontal.value\n ? { width: `${split.value}%` }\n : { height: `${split.value}%` },\n)\n\nconst panelBStyle = computed(() =>\n isHorizontal.value\n ? { width: `${100 - split.value}%` }\n : { height: `${100 - split.value}%` },\n)\n\nfunction onPointerDown(e: PointerEvent) {\n dragging.value = true\n ;(e.target as HTMLElement).setPointerCapture(e.pointerId)\n}\n\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value || !containerRef.value) return\n\n const rect = containerRef.value.getBoundingClientRect()\n let pct: number\n\n if (isHorizontal.value) {\n pct = ((e.clientX - rect.left) / rect.width) * 100\n } else {\n pct = ((e.clientY - rect.top) / rect.height) * 100\n }\n\n split.value = Math.min(props.max, Math.max(props.min, pct))\n}\n\nfunction onPointerUp() {\n dragging.value = false\n}\n\nonBeforeUnmount(() => {\n dragging.value = false\n})\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"flex overflow-hidden\"\n :class=\"[\n isHorizontal ? 'flex-row' : 'flex-col',\n dragging && 'select-none',\n ]\"\n style=\"height: 100%\"\n >\n <div class=\"overflow-auto\" :style=\"panelAStyle\">\n <slot name=\"first\" />\n </div>\n\n <div\n class=\"z-10 flex shrink-0 items-center justify-center transition-colors\"\n :class=\"[\n isHorizontal\n ? 'w-2 cursor-col-resize flex-col'\n : 'h-2 cursor-row-resize flex-row',\n dragging ? 'bg-primary/20' : 'bg-outline-variant/40 hover:bg-primary/12',\n ]\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n >\n <div\n class=\"rounded-full bg-outline\"\n :class=\"isHorizontal ? 'h-6 w-1' : 'h-1 w-6'\"\n />\n </div>\n\n <div class=\"overflow-auto\" :style=\"panelBStyle\">\n <slot name=\"second\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MSpinner from './MSpinner.vue'\n\nexport interface SpotlightResult {\n id: string | number\n title: string\n description?: string\n icon?: string\n category?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n results?: SpotlightResult[]\n placeholder?: string\n loading?: boolean\n noResultsText?: string\n hotkey?: string\n debounce?: number\n }>(),\n {\n results: () => [],\n placeholder: 'Buscar...',\n loading: false,\n noResultsText: 'No se encontraron resultados',\n hotkey: '/',\n debounce: 0,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n search: [string]\n select: [SpotlightResult]\n}>()\n\nconst query = ref('')\nconst activeIndex = ref(0)\nconst inputRef = ref<HTMLInputElement | null>(null)\nlet debounceTimer: ReturnType<typeof setTimeout> | null = null\n\nconst hasQuery = computed(() => query.value.trim().length > 0)\n\nconst grouped = computed(() => {\n const map = new Map<string, SpotlightResult[]>()\n for (const r of props.results) {\n const cat = r.category ?? ''\n if (!map.has(cat)) map.set(cat, [])\n map.get(cat)!.push(r)\n }\n return map\n})\n\nfunction close() {\n query.value = ''\n activeIndex.value = 0\n emit('update:modelValue', false)\n}\n\nfunction selectResult(result: SpotlightResult) {\n emit('select', result)\n close()\n}\n\nfunction emitSearch() {\n if (debounceTimer) clearTimeout(debounceTimer)\n if (props.debounce > 0) {\n debounceTimer = setTimeout(() => emit('search', query.value), props.debounce)\n } else {\n emit('search', query.value)\n }\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n const len = props.results.length\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n activeIndex.value = len ? (activeIndex.value + 1) % len : 0\n scrollToActive()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n activeIndex.value = len ? (activeIndex.value - 1 + len) % len : 0\n scrollToActive()\n } else if (e.key === 'Enter' && len) {\n e.preventDefault()\n selectResult(props.results[activeIndex.value]!)\n } else if (e.key === 'Escape') {\n close()\n }\n}\n\nfunction scrollToActive() {\n nextTick(() => {\n const el = document.querySelector('[data-spot-active=\"true\"]')\n el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction onGlobalKeydown(e: KeyboardEvent) {\n const tag = (e.target as HTMLElement).tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable) return\n if (e.key === props.hotkey && !e.metaKey && !e.ctrlKey && !e.altKey) {\n e.preventDefault()\n emit('update:modelValue', true)\n }\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.body.style.overflow = 'hidden'\n nextTick(() => inputRef.value?.focus())\n } else {\n document.body.style.overflow = ''\n }\n },\n)\n\nwatch(query, () => {\n activeIndex.value = 0\n emitSearch()\n})\n\nonMounted(() => document.addEventListener('keydown', onGlobalKeydown))\nonBeforeUnmount(() => {\n document.removeEventListener('keydown', onGlobalKeydown)\n if (debounceTimer) clearTimeout(debounceTimer)\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-spot\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-black/50 pt-[12vh]\"\n @click.self=\"close\"\n >\n <div class=\"spot-box flex w-full max-w-xl flex-col overflow-hidden rounded-2xl bg-surface-container-high shadow-elevation-3\">\n <!-- Search bar -->\n <div class=\"flex items-center gap-3 px-5 py-1\">\n <MIcon name=\"search\" :size=\"24\" class=\"shrink-0 text-primary\" />\n <input\n ref=\"inputRef\"\n v-model=\"query\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"h-14 flex-1 bg-transparent text-title-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n @keydown=\"onKeydown\"\n />\n <MSpinner v-if=\"loading\" :size=\"20\" class=\"shrink-0 text-primary\" />\n <button\n v-else-if=\"hasQuery\"\n type=\"button\"\n class=\"flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-full text-on-surface-variant hover:bg-on-surface/8\"\n @click=\"query = ''\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <!-- Results -->\n <div v-if=\"hasQuery\" class=\"max-h-96 overflow-y-auto border-t border-outline-variant\">\n <template v-if=\"results.length\">\n <template v-for=\"[category, items] in grouped\" :key=\"category\">\n <p v-if=\"category\" class=\"px-5 pt-4 pb-1 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ category }}\n </p>\n <button\n v-for=\"item in items\"\n :key=\"item.id\"\n type=\"button\"\n :data-spot-active=\"results.indexOf(item) === activeIndex || undefined\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-5 py-3 text-left transition-colors\"\n :class=\"results.indexOf(item) === activeIndex ? 'bg-primary/12' : 'hover:bg-on-surface/4'\"\n @click=\"selectResult(item)\"\n @pointerenter=\"activeIndex = results.indexOf(item)\"\n >\n <div\n v-if=\"item.icon\"\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary-container\"\n >\n <MIcon :name=\"item.icon\" :size=\"20\" class=\"text-on-primary-container\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-body-medium text-on-surface\">{{ item.title }}</p>\n <p v-if=\"item.description\" class=\"truncate text-body-small text-on-surface-variant\">\n {{ item.description }}\n </p>\n </div>\n <MIcon name=\"arrow_forward\" :size=\"16\" class=\"shrink-0 text-on-surface-variant/40\" />\n </button>\n </template>\n </template>\n <div v-else-if=\"!loading\" class=\"flex flex-col items-center gap-2 py-10\">\n <MIcon name=\"search_off\" :size=\"40\" class=\"text-on-surface-variant/40\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ noResultsText }}</p>\n </div>\n </div>\n\n <!-- Hints -->\n <div class=\"flex items-center gap-4 border-t border-outline-variant px-5 py-2\">\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↑↓</kbd> navegar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↵</kbd> abrir\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">esc</kbd> cerrar\n </span>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-spot-enter-active,\n.m3-spot-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-spot-enter-from,\n.m3-spot-leave-to {\n opacity: 0;\n}\n.m3-spot-enter-active .spot-box,\n.m3-spot-leave-active .spot-box {\n transition: transform 0.15s ease, opacity 0.15s ease;\n}\n.m3-spot-enter-from .spot-box {\n transform: scale(0.96) translateY(-8px);\n opacity: 0;\n}\n.m3-spot-leave-to .spot-box {\n transform: scale(0.98);\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MSpinner from './MSpinner.vue'\n\nexport interface SpotlightResult {\n id: string | number\n title: string\n description?: string\n icon?: string\n category?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n results?: SpotlightResult[]\n placeholder?: string\n loading?: boolean\n noResultsText?: string\n hotkey?: string\n debounce?: number\n }>(),\n {\n results: () => [],\n placeholder: 'Buscar...',\n loading: false,\n noResultsText: 'No se encontraron resultados',\n hotkey: '/',\n debounce: 0,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n search: [string]\n select: [SpotlightResult]\n}>()\n\nconst query = ref('')\nconst activeIndex = ref(0)\nconst inputRef = ref<HTMLInputElement | null>(null)\nlet debounceTimer: ReturnType<typeof setTimeout> | null = null\n\nconst hasQuery = computed(() => query.value.trim().length > 0)\n\nconst grouped = computed(() => {\n const map = new Map<string, SpotlightResult[]>()\n for (const r of props.results) {\n const cat = r.category ?? ''\n if (!map.has(cat)) map.set(cat, [])\n map.get(cat)!.push(r)\n }\n return map\n})\n\nfunction close() {\n query.value = ''\n activeIndex.value = 0\n emit('update:modelValue', false)\n}\n\nfunction selectResult(result: SpotlightResult) {\n emit('select', result)\n close()\n}\n\nfunction emitSearch() {\n if (debounceTimer) clearTimeout(debounceTimer)\n if (props.debounce > 0) {\n debounceTimer = setTimeout(() => emit('search', query.value), props.debounce)\n } else {\n emit('search', query.value)\n }\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n const len = props.results.length\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n activeIndex.value = len ? (activeIndex.value + 1) % len : 0\n scrollToActive()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n activeIndex.value = len ? (activeIndex.value - 1 + len) % len : 0\n scrollToActive()\n } else if (e.key === 'Enter' && len) {\n e.preventDefault()\n selectResult(props.results[activeIndex.value]!)\n } else if (e.key === 'Escape') {\n close()\n }\n}\n\nfunction scrollToActive() {\n nextTick(() => {\n const el = document.querySelector('[data-spot-active=\"true\"]')\n el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction onGlobalKeydown(e: KeyboardEvent) {\n const tag = (e.target as HTMLElement).tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable) return\n if (e.key === props.hotkey && !e.metaKey && !e.ctrlKey && !e.altKey) {\n e.preventDefault()\n emit('update:modelValue', true)\n }\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.body.style.overflow = 'hidden'\n nextTick(() => inputRef.value?.focus())\n } else {\n document.body.style.overflow = ''\n }\n },\n)\n\nwatch(query, () => {\n activeIndex.value = 0\n emitSearch()\n})\n\nonMounted(() => document.addEventListener('keydown', onGlobalKeydown))\nonBeforeUnmount(() => {\n document.removeEventListener('keydown', onGlobalKeydown)\n if (debounceTimer) clearTimeout(debounceTimer)\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-spot\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-black/50 pt-[12vh]\"\n @click.self=\"close\"\n >\n <div class=\"spot-box flex w-full max-w-xl flex-col overflow-hidden rounded-2xl bg-surface-container-high shadow-elevation-3\">\n <!-- Search bar -->\n <div class=\"flex items-center gap-3 px-5 py-1\">\n <MIcon name=\"search\" :size=\"24\" class=\"shrink-0 text-primary\" />\n <input\n ref=\"inputRef\"\n v-model=\"query\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"h-14 flex-1 bg-transparent text-title-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n @keydown=\"onKeydown\"\n />\n <MSpinner v-if=\"loading\" :size=\"20\" class=\"shrink-0 text-primary\" />\n <button\n v-else-if=\"hasQuery\"\n type=\"button\"\n class=\"flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-full text-on-surface-variant hover:bg-on-surface/8\"\n @click=\"query = ''\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <!-- Results -->\n <div v-if=\"hasQuery\" class=\"max-h-96 overflow-y-auto border-t border-outline-variant\">\n <template v-if=\"results.length\">\n <template v-for=\"[category, items] in grouped\" :key=\"category\">\n <p v-if=\"category\" class=\"px-5 pt-4 pb-1 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ category }}\n </p>\n <button\n v-for=\"item in items\"\n :key=\"item.id\"\n type=\"button\"\n :data-spot-active=\"results.indexOf(item) === activeIndex || undefined\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-5 py-3 text-left transition-colors\"\n :class=\"results.indexOf(item) === activeIndex ? 'bg-primary/12' : 'hover:bg-on-surface/4'\"\n @click=\"selectResult(item)\"\n @pointerenter=\"activeIndex = results.indexOf(item)\"\n >\n <div\n v-if=\"item.icon\"\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary-container\"\n >\n <MIcon :name=\"item.icon\" :size=\"20\" class=\"text-on-primary-container\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-body-medium text-on-surface\">{{ item.title }}</p>\n <p v-if=\"item.description\" class=\"truncate text-body-small text-on-surface-variant\">\n {{ item.description }}\n </p>\n </div>\n <MIcon name=\"arrow_forward\" :size=\"16\" class=\"shrink-0 text-on-surface-variant/40\" />\n </button>\n </template>\n </template>\n <div v-else-if=\"!loading\" class=\"flex flex-col items-center gap-2 py-10\">\n <MIcon name=\"search_off\" :size=\"40\" class=\"text-on-surface-variant/40\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ noResultsText }}</p>\n </div>\n </div>\n\n <!-- Hints -->\n <div class=\"flex items-center gap-4 border-t border-outline-variant px-5 py-2\">\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↑↓</kbd> navegar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↵</kbd> abrir\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">esc</kbd> cerrar\n </span>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-spot-enter-active,\n.m3-spot-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-spot-enter-from,\n.m3-spot-leave-to {\n opacity: 0;\n}\n.m3-spot-enter-active .spot-box,\n.m3-spot-leave-active .spot-box {\n transition: transform 0.15s ease, opacity 0.15s ease;\n}\n.m3-spot-enter-from .spot-box {\n transform: scale(0.96) translateY(-8px);\n opacity: 0;\n}\n.m3-spot-leave-to .spot-box {\n transform: scale(0.98);\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n direction?: 'column' | 'row'\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline'\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'\n wrap?: boolean\n divider?: boolean\n inline?: boolean\n }>(),\n { direction: 'column', gap: 'md', align: 'stretch', justify: 'start', wrap: false, divider: false, inline: false },\n)\n\nconst gapClasses: Record<string, string> = {\n none: 'gap-0',\n xs: 'gap-1',\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n xl: 'gap-8',\n}\n\nconst alignClasses: Record<string, string> = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n baseline: 'items-baseline',\n}\n\nconst justifyClasses: Record<string, string> = {\n start: 'justify-start',\n center: 'justify-center',\n end: 'justify-end',\n between: 'justify-between',\n around: 'justify-around',\n evenly: 'justify-evenly',\n}\n\nconst classes = computed(() => [\n props.inline ? 'inline-flex' : 'flex',\n props.direction === 'row' ? 'flex-row' : 'flex-col',\n !props.divider && gapClasses[props.gap],\n alignClasses[props.align],\n justifyClasses[props.justify],\n props.wrap && 'flex-wrap',\n])\n\nconst dividerClass = computed(() =>\n props.direction === 'row' ? 'w-px self-stretch bg-outline-variant' : 'h-px w-full bg-outline-variant',\n)\n</script>\n\n<template>\n <div :class=\"classes\">\n <template v-if=\"divider\">\n <template v-for=\"(_, i) in ($slots.default?.() ?? [])\" :key=\"i\">\n <div v-if=\"i > 0\" :class=\"dividerClass\" role=\"separator\" />\n <component :is=\"_\" />\n </template>\n </template>\n <slot v-else />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n direction?: 'column' | 'row'\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline'\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'\n wrap?: boolean\n divider?: boolean\n inline?: boolean\n }>(),\n { direction: 'column', gap: 'md', align: 'stretch', justify: 'start', wrap: false, divider: false, inline: false },\n)\n\nconst gapClasses: Record<string, string> = {\n none: 'gap-0',\n xs: 'gap-1',\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n xl: 'gap-8',\n}\n\nconst alignClasses: Record<string, string> = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n baseline: 'items-baseline',\n}\n\nconst justifyClasses: Record<string, string> = {\n start: 'justify-start',\n center: 'justify-center',\n end: 'justify-end',\n between: 'justify-between',\n around: 'justify-around',\n evenly: 'justify-evenly',\n}\n\nconst classes = computed(() => [\n props.inline ? 'inline-flex' : 'flex',\n props.direction === 'row' ? 'flex-row' : 'flex-col',\n !props.divider && gapClasses[props.gap],\n alignClasses[props.align],\n justifyClasses[props.justify],\n props.wrap && 'flex-wrap',\n])\n\nconst dividerClass = computed(() =>\n props.direction === 'row' ? 'w-px self-stretch bg-outline-variant' : 'h-px w-full bg-outline-variant',\n)\n</script>\n\n<template>\n <div :class=\"classes\">\n <template v-if=\"divider\">\n <template v-for=\"(_, i) in ($slots.default?.() ?? [])\" :key=\"i\">\n <div v-if=\"i > 0\" :class=\"dividerClass\" role=\"separator\" />\n <component :is=\"_\" />\n </template>\n </template>\n <slot v-else />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n title: string\n value: string | number\n icon?: string\n trend?: number\n trendLabel?: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n loading?: boolean\n}>(), { color: 'primary' })\n\nconst iconBg: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container',\n secondary: 'bg-secondary-container text-on-secondary-container',\n tertiary: 'bg-tertiary-container text-on-tertiary-container',\n error: 'bg-error-container text-on-error-container',\n success: 'bg-success-container text-on-success-container',\n}\n\nconst trendColor = computed(() => {\n if (props.trend == null) return ''\n return props.trend > 0 ? 'text-success' : props.trend < 0 ? 'text-error' : 'text-on-surface-variant'\n})\nconst trendIcon = computed(() => {\n if (props.trend == null) return ''\n return props.trend > 0 ? 'trending_up' : props.trend < 0 ? 'trending_down' : 'trending_flat'\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-3 rounded-lg border border-outline-variant bg-surface-container-lowest p-5\">\n <div class=\"flex items-start justify-between\">\n <div class=\"flex flex-col gap-1\">\n <span class=\"text-label-large text-on-surface-variant\">{{ title }}</span>\n <template v-if=\"loading\">\n <div class=\"h-8 w-24 animate-pulse rounded-md bg-on-surface/10\" />\n </template>\n <span v-else class=\"text-headline-medium font-medium text-on-surface\">{{ value }}</span>\n </div>\n <div v-if=\"icon\" class=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl\" :class=\"iconBg[color]\">\n <MIcon :name=\"icon\" :size=\"24\" />\n </div>\n </div>\n <div v-if=\"trend != null || trendLabel || $slots.footer\" class=\"flex items-center gap-2\">\n <span v-if=\"trend != null\" class=\"inline-flex items-center gap-0.5 text-label-medium font-medium\" :class=\"trendColor\">\n <MIcon :name=\"trendIcon\" :size=\"16\" />\n {{ trend > 0 ? '+' : '' }}{{ trend }}%\n </span>\n <span v-if=\"trendLabel\" class=\"text-label-medium text-on-surface-variant\">{{ trendLabel }}</span>\n <slot name=\"footer\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n title: string\n value: string | number\n icon?: string\n trend?: number\n trendLabel?: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n loading?: boolean\n}>(), { color: 'primary' })\n\nconst iconBg: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container',\n secondary: 'bg-secondary-container text-on-secondary-container',\n tertiary: 'bg-tertiary-container text-on-tertiary-container',\n error: 'bg-error-container text-on-error-container',\n success: 'bg-success-container text-on-success-container',\n}\n\nconst trendColor = computed(() => {\n if (props.trend == null) return ''\n return props.trend > 0 ? 'text-success' : props.trend < 0 ? 'text-error' : 'text-on-surface-variant'\n})\nconst trendIcon = computed(() => {\n if (props.trend == null) return ''\n return props.trend > 0 ? 'trending_up' : props.trend < 0 ? 'trending_down' : 'trending_flat'\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-3 rounded-lg border border-outline-variant bg-surface-container-lowest p-5\">\n <div class=\"flex items-start justify-between\">\n <div class=\"flex flex-col gap-1\">\n <span class=\"text-label-large text-on-surface-variant\">{{ title }}</span>\n <template v-if=\"loading\">\n <div class=\"h-8 w-24 animate-pulse rounded-md bg-on-surface/10\" />\n </template>\n <span v-else class=\"text-headline-medium font-medium text-on-surface\">{{ value }}</span>\n </div>\n <div v-if=\"icon\" class=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl\" :class=\"iconBg[color]\">\n <MIcon :name=\"icon\" :size=\"24\" />\n </div>\n </div>\n <div v-if=\"trend != null || trendLabel || $slots.footer\" class=\"flex items-center gap-2\">\n <span v-if=\"trend != null\" class=\"inline-flex items-center gap-0.5 text-label-medium font-medium\" :class=\"trendColor\">\n <MIcon :name=\"trendIcon\" :size=\"16\" />\n {{ trend > 0 ? '+' : '' }}{{ trend }}%\n </span>\n <span v-if=\"trendLabel\" class=\"text-label-medium text-on-surface-variant\">{{ trendLabel }}</span>\n <slot name=\"footer\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface StepItem {\n label: string\n description?: string\n icon?: string\n optional?: boolean\n error?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n steps: StepItem[]\n modelValue: number\n direction?: 'horizontal' | 'vertical'\n linear?: boolean\n}>(), { direction: 'horizontal', linear: true })\n\nconst emit = defineEmits<{ 'update:modelValue': [number] }>()\n\nfunction stepState(index: number) {\n if (props.steps[index]?.error) return 'error'\n if (index < props.modelValue) return 'completed'\n if (index === props.modelValue) return 'active'\n return 'inactive'\n}\n\nconst canClick = computed(() => !props.linear)\n\nfunction select(index: number) {\n if (canClick.value) emit('update:modelValue', index)\n}\n</script>\n\n<template>\n <!-- Horizontal -->\n <div\n v-if=\"direction === 'horizontal'\"\n class=\"flex w-full items-start\"\n >\n <template v-for=\"(step, i) in steps\" :key=\"i\">\n <!-- Step -->\n <div\n class=\"flex flex-col items-center gap-2\"\n :class=\"canClick && stepState(i) !== 'active' ? 'cursor-pointer' : ''\"\n @click=\"select(i)\"\n >\n <!-- Circle -->\n <div\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-label-large font-medium transition-colors duration-200\"\n :class=\"{\n 'bg-primary text-on-primary': stepState(i) === 'active' || stepState(i) === 'completed',\n 'bg-error text-on-error': stepState(i) === 'error',\n 'bg-surface-container-highest text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n <MIcon v-if=\"stepState(i) === 'completed'\" name=\"check\" :size=\"20\" />\n <MIcon v-else-if=\"stepState(i) === 'error'\" name=\"priority_high\" :size=\"20\" />\n <MIcon v-else-if=\"step.icon\" :name=\"step.icon\" :size=\"20\" />\n <span v-else>{{ i + 1 }}</span>\n </div>\n\n <!-- Label -->\n <div class=\"flex flex-col items-center text-center\">\n <span\n class=\"text-label-large\"\n :class=\"{\n 'font-medium text-on-surface': stepState(i) === 'active' || stepState(i) === 'completed',\n 'text-error': stepState(i) === 'error',\n 'text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n {{ step.label }}\n </span>\n <span v-if=\"step.description\" class=\"text-body-small text-on-surface-variant\">\n {{ step.description }}\n </span>\n <span v-if=\"step.optional\" class=\"text-body-small text-on-surface-variant\">\n Opcional\n </span>\n </div>\n </div>\n\n <!-- Connector -->\n <div\n v-if=\"i < steps.length - 1\"\n class=\"mt-[18px] flex flex-1 items-center px-2\"\n >\n <div\n class=\"h-[1px] w-full transition-colors duration-300\"\n :class=\"i < modelValue ? 'bg-primary' : 'bg-outline-variant'\"\n />\n </div>\n </template>\n </div>\n\n <!-- Vertical -->\n <div v-else class=\"flex flex-col\">\n <template v-for=\"(step, i) in steps\" :key=\"i\">\n <div class=\"flex gap-4\">\n <!-- Left column: circle + connector -->\n <div class=\"flex flex-col items-center\">\n <div\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-label-large font-medium transition-colors duration-200\"\n :class=\"[\n {\n 'bg-primary text-on-primary': stepState(i) === 'active' || stepState(i) === 'completed',\n 'bg-error text-on-error': stepState(i) === 'error',\n 'bg-surface-container-highest text-on-surface-variant': stepState(i) === 'inactive',\n },\n canClick && stepState(i) !== 'active' ? 'cursor-pointer' : '',\n ]\"\n @click=\"select(i)\"\n >\n <MIcon v-if=\"stepState(i) === 'completed'\" name=\"check\" :size=\"20\" />\n <MIcon v-else-if=\"stepState(i) === 'error'\" name=\"priority_high\" :size=\"20\" />\n <MIcon v-else-if=\"step.icon\" :name=\"step.icon\" :size=\"20\" />\n <span v-else>{{ i + 1 }}</span>\n </div>\n <!-- Vertical connector -->\n <div\n v-if=\"i < steps.length - 1\"\n class=\"my-1 w-[1px] flex-1 transition-colors duration-300\"\n :class=\"i < modelValue ? 'bg-primary' : 'bg-outline-variant'\"\n style=\"min-height: 24px\"\n />\n </div>\n\n <!-- Right column: label + content -->\n <div\n class=\"pb-6\"\n :class=\"canClick && stepState(i) !== 'active' ? 'cursor-pointer' : ''\"\n @click=\"select(i)\"\n >\n <span\n class=\"text-label-large\"\n :class=\"{\n 'font-medium text-on-surface': stepState(i) === 'active' || stepState(i) === 'completed',\n 'text-error': stepState(i) === 'error',\n 'text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n {{ step.label }}\n </span>\n <p v-if=\"step.description\" class=\"mt-0.5 text-body-small text-on-surface-variant\">\n {{ step.description }}\n </p>\n <p v-if=\"step.optional\" class=\"text-body-small text-on-surface-variant\">\n Opcional\n </p>\n\n <!-- Slot for step content when active -->\n <div v-if=\"stepState(i) === 'active' && $slots[`step-${i}`]\" class=\"mt-3\">\n <slot :name=\"`step-${i}`\" />\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface StepItem {\n label: string\n description?: string\n icon?: string\n optional?: boolean\n error?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n steps: StepItem[]\n modelValue: number\n direction?: 'horizontal' | 'vertical'\n linear?: boolean\n}>(), { direction: 'horizontal', linear: true })\n\nconst emit = defineEmits<{ 'update:modelValue': [number] }>()\n\nfunction stepState(index: number) {\n if (props.steps[index]?.error) return 'error'\n if (index < props.modelValue) return 'completed'\n if (index === props.modelValue) return 'active'\n return 'inactive'\n}\n\nconst canClick = computed(() => !props.linear)\n\nfunction select(index: number) {\n if (canClick.value) emit('update:modelValue', index)\n}\n</script>\n\n<template>\n <!-- Horizontal -->\n <div\n v-if=\"direction === 'horizontal'\"\n class=\"flex w-full items-start\"\n >\n <template v-for=\"(step, i) in steps\" :key=\"i\">\n <!-- Step -->\n <div\n class=\"flex flex-col items-center gap-2\"\n :class=\"canClick && stepState(i) !== 'active' ? 'cursor-pointer' : ''\"\n @click=\"select(i)\"\n >\n <!-- Circle -->\n <div\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-label-large font-medium transition-colors duration-200\"\n :class=\"{\n 'bg-primary text-on-primary': stepState(i) === 'active' || stepState(i) === 'completed',\n 'bg-error text-on-error': stepState(i) === 'error',\n 'bg-surface-container-highest text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n <MIcon v-if=\"stepState(i) === 'completed'\" name=\"check\" :size=\"20\" />\n <MIcon v-else-if=\"stepState(i) === 'error'\" name=\"priority_high\" :size=\"20\" />\n <MIcon v-else-if=\"step.icon\" :name=\"step.icon\" :size=\"20\" />\n <span v-else>{{ i + 1 }}</span>\n </div>\n\n <!-- Label -->\n <div class=\"flex flex-col items-center text-center\">\n <span\n class=\"text-label-large\"\n :class=\"{\n 'font-medium text-on-surface': stepState(i) === 'active' || stepState(i) === 'completed',\n 'text-error': stepState(i) === 'error',\n 'text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n {{ step.label }}\n </span>\n <span v-if=\"step.description\" class=\"text-body-small text-on-surface-variant\">\n {{ step.description }}\n </span>\n <span v-if=\"step.optional\" class=\"text-body-small text-on-surface-variant\">\n Opcional\n </span>\n </div>\n </div>\n\n <!-- Connector -->\n <div\n v-if=\"i < steps.length - 1\"\n class=\"mt-[18px] flex flex-1 items-center px-2\"\n >\n <div\n class=\"h-[1px] w-full transition-colors duration-300\"\n :class=\"i < modelValue ? 'bg-primary' : 'bg-outline-variant'\"\n />\n </div>\n </template>\n </div>\n\n <!-- Vertical -->\n <div v-else class=\"flex flex-col\">\n <template v-for=\"(step, i) in steps\" :key=\"i\">\n <div class=\"flex gap-4\">\n <!-- Left column: circle + connector -->\n <div class=\"flex flex-col items-center\">\n <div\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-label-large font-medium transition-colors duration-200\"\n :class=\"[\n {\n 'bg-primary text-on-primary': stepState(i) === 'active' || stepState(i) === 'completed',\n 'bg-error text-on-error': stepState(i) === 'error',\n 'bg-surface-container-highest text-on-surface-variant': stepState(i) === 'inactive',\n },\n canClick && stepState(i) !== 'active' ? 'cursor-pointer' : '',\n ]\"\n @click=\"select(i)\"\n >\n <MIcon v-if=\"stepState(i) === 'completed'\" name=\"check\" :size=\"20\" />\n <MIcon v-else-if=\"stepState(i) === 'error'\" name=\"priority_high\" :size=\"20\" />\n <MIcon v-else-if=\"step.icon\" :name=\"step.icon\" :size=\"20\" />\n <span v-else>{{ i + 1 }}</span>\n </div>\n <!-- Vertical connector -->\n <div\n v-if=\"i < steps.length - 1\"\n class=\"my-1 w-[1px] flex-1 transition-colors duration-300\"\n :class=\"i < modelValue ? 'bg-primary' : 'bg-outline-variant'\"\n style=\"min-height: 24px\"\n />\n </div>\n\n <!-- Right column: label + content -->\n <div\n class=\"pb-6\"\n :class=\"canClick && stepState(i) !== 'active' ? 'cursor-pointer' : ''\"\n @click=\"select(i)\"\n >\n <span\n class=\"text-label-large\"\n :class=\"{\n 'font-medium text-on-surface': stepState(i) === 'active' || stepState(i) === 'completed',\n 'text-error': stepState(i) === 'error',\n 'text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n {{ step.label }}\n </span>\n <p v-if=\"step.description\" class=\"mt-0.5 text-body-small text-on-surface-variant\">\n {{ step.description }}\n </p>\n <p v-if=\"step.optional\" class=\"text-body-small text-on-surface-variant\">\n Opcional\n </p>\n\n <!-- Slot for step content when active -->\n <div v-if=\"stepState(i) === 'active' && $slots[`step-${i}`]\" class=\"mt-3\">\n <slot :name=\"`step-${i}`\" />\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{ modelValue: boolean; disabled?: boolean; label?: string }>(),\n { disabled: false },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\n// All thumb transforms live here so each direction can use its own easing curve.\n// translateY(-50%) vertically centres the 24px thumb in the 32px track.\n// ON → spring cubic-bezier: overshoots ~2px then settles → satisfying \"click\"\n// OFF → M3 standard decelerate: clean snap back, no undershoot\nconst thumbStyle = computed(() => ({\n transform: props.modelValue\n ? 'translateY(-50%) translateX(18px) scale(1)'\n : 'translateY(-50%) translateX(0px) scale(0.667)',\n transition: props.modelValue\n ? 'transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1), background-color 280ms ease'\n : 'transform 240ms cubic-bezier(0.2, 0, 0, 1), background-color 240ms ease',\n}))\n</script>\n\n<template>\n <label\n class=\"inline-flex items-center gap-3 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span\n class=\"relative inline-flex h-8 w-[52px] shrink-0 items-center rounded-full border-2 transition-colors duration-200\"\n :class=\"modelValue ? 'border-primary bg-primary' : 'border-outline bg-surface-container-highest'\"\n >\n <input\n type=\"checkbox\"\n class=\"sr-only\"\n :checked=\"modelValue\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', !modelValue)\"\n />\n\n <!-- Thumb: position + size animated via inline style (allows per-direction easing) -->\n <span\n class=\"absolute left-1 top-1/2 flex h-6 w-6 items-center justify-center rounded-full will-change-transform\"\n :class=\"modelValue ? 'bg-on-primary shadow-sm' : 'bg-outline'\"\n :style=\"thumbStyle\"\n >\n <Transition\n enter-active-class=\"transition-opacity duration-150 delay-[120ms]\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <MIcon v-if=\"modelValue\" name=\"check\" :size=\"14\" class=\"text-primary\" />\n </Transition>\n </span>\n </span>\n <span v-if=\"label\" class=\"text-body-large text-on-surface\">{{ label }}</span>\n </label>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{ modelValue: boolean; disabled?: boolean; label?: string }>(),\n { disabled: false },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\n// All thumb transforms live here so each direction can use its own easing curve.\n// translateY(-50%) vertically centres the 24px thumb in the 32px track.\n// ON → spring cubic-bezier: overshoots ~2px then settles → satisfying \"click\"\n// OFF → M3 standard decelerate: clean snap back, no undershoot\nconst thumbStyle = computed(() => ({\n transform: props.modelValue\n ? 'translateY(-50%) translateX(18px) scale(1)'\n : 'translateY(-50%) translateX(0px) scale(0.667)',\n transition: props.modelValue\n ? 'transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1), background-color 280ms ease'\n : 'transform 240ms cubic-bezier(0.2, 0, 0, 1), background-color 240ms ease',\n}))\n</script>\n\n<template>\n <label\n class=\"inline-flex items-center gap-3 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span\n class=\"relative inline-flex h-8 w-[52px] shrink-0 items-center rounded-full border-2 transition-colors duration-200\"\n :class=\"modelValue ? 'border-primary bg-primary' : 'border-outline bg-surface-container-highest'\"\n >\n <input\n type=\"checkbox\"\n class=\"sr-only\"\n :checked=\"modelValue\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', !modelValue)\"\n />\n\n <!-- Thumb: position + size animated via inline style (allows per-direction easing) -->\n <span\n class=\"absolute left-1 top-1/2 flex h-6 w-6 items-center justify-center rounded-full will-change-transform\"\n :class=\"modelValue ? 'bg-on-primary shadow-sm' : 'bg-outline'\"\n :style=\"thumbStyle\"\n >\n <Transition\n enter-active-class=\"transition-opacity duration-150 delay-[120ms]\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <MIcon v-if=\"modelValue\" name=\"check\" :size=\"14\" class=\"text-primary\" />\n </Transition>\n </span>\n </span>\n <span v-if=\"label\" class=\"text-body-large text-on-surface\">{{ label }}</span>\n </label>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport MPagination from './MPagination.vue'\n\nexport interface TableColumn {\n key: string\n label: string\n sortable?: boolean\n width?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport interface TableFetchParams {\n page: number\n perPage: number\n search: string\n sortKey: string\n sortDir: 'asc' | 'desc' | ''\n}\n\n// Static widths so skeleton markup is stable across re-renders\nconst SKEL = [65, 80, 50, 75, 90, 55, 70, 85, 60, 78, 88, 52, 70, 83, 58]\n\nconst props = withDefaults(\n defineProps<{\n columns: TableColumn[]\n rows: Record<string, any>[]\n loading?: boolean\n emptyText?: string\n rowKey?: string\n selectable?: boolean\n modelValue?: Record<string, any>[]\n perPage?: number\n searchable?: boolean\n /** Emit `fetch` instead of filtering locally. Requires :total. */\n serverSide?: boolean\n total?: number\n page?: number\n }>(),\n {\n loading: false,\n emptyText: 'Sin resultados',\n rowKey: 'id',\n selectable: false,\n modelValue: () => [],\n perPage: 10,\n searchable: true,\n serverSide: false,\n total: 0,\n page: 1,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [Record<string, any>[]]\n 'update:page': [number]\n fetch: [TableFetchParams]\n}>()\n\n// ── Search ─────────────────────────────────────────────────────────────────\nconst search = ref('')\n\n// ── Sort ───────────────────────────────────────────────────────────────────\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) {\n sortKey.value = key\n sortDir.value = 'asc'\n } else if (sortDir.value === 'asc') {\n sortDir.value = 'desc'\n } else {\n sortKey.value = ''\n sortDir.value = ''\n }\n}\n\n// ── Pagination ─────────────────────────────────────────────────────────────\nconst internalPage = ref(1)\n\nconst currentPage = computed({\n get: () => (props.serverSide ? (props.page ?? 1) : internalPage.value),\n set: (val: number) => {\n if (props.serverSide) emit('update:page', val)\n else internalPage.value = val\n },\n})\n\n// ── Client-side data processing ────────────────────────────────────────────\nconst processedRows = computed(() => {\n if (props.serverSide) return props.rows\n\n let result = props.rows\n\n if (search.value.trim()) {\n const q = search.value.toLowerCase()\n result = result.filter((row) =>\n props.columns.some((col) => {\n const val = row[col.key]\n return val != null && String(val).toLowerCase().includes(q)\n }),\n )\n }\n\n if (sortKey.value && sortDir.value) {\n const key = sortKey.value\n const dir = sortDir.value\n result = [...result].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, {\n numeric: true,\n sensitivity: 'base',\n })\n return dir === 'asc' ? cmp : -cmp\n })\n }\n\n return result\n})\n\nconst totalCount = computed(() =>\n props.serverSide ? (props.total ?? 0) : processedRows.value.length,\n)\n\nconst visibleRows = computed(() => {\n if (props.serverSide) return props.rows\n const start = (currentPage.value - 1) * props.perPage\n return processedRows.value.slice(start, start + props.perPage)\n})\n\nwatch([search, sortKey, sortDir], () => {\n if (!props.serverSide) internalPage.value = 1\n})\n\n// ── Server-side fetch ──────────────────────────────────────────────────────\nconst mounted = ref(false)\n\nfunction emitFetch() {\n emit('fetch', {\n page: currentPage.value,\n perPage: props.perPage,\n search: search.value,\n sortKey: sortKey.value,\n sortDir: sortDir.value,\n })\n}\n\nonMounted(() => {\n mounted.value = true\n if (props.serverSide) emitFetch()\n})\n\nwatch([search, sortKey, sortDir], () => {\n if (!props.serverSide || !mounted.value) return\n internalPage.value = 1\n emitFetch()\n})\n\nwatch(currentPage, () => {\n if (!props.serverSide || !mounted.value) return\n emitFetch()\n})\n\n// ── Row selection ──────────────────────────────────────────────────────────\nconst selected = computed({\n get: () => props.modelValue ?? [],\n set: (val) => emit('update:modelValue', val),\n})\n\nfunction rowId(row: Record<string, any>) {\n return row[props.rowKey]\n}\nfunction isSelected(row: Record<string, any>) {\n return selected.value.some((r) => rowId(r) === rowId(row))\n}\nfunction toggleRow(row: Record<string, any>) {\n if (isSelected(row)) selected.value = selected.value.filter((r) => rowId(r) !== rowId(row))\n else selected.value = [...selected.value, row]\n}\n\nconst allOnPageSelected = computed(\n () => visibleRows.value.length > 0 && visibleRows.value.every((r) => isSelected(r)),\n)\nconst someOnPageSelected = computed(\n () => visibleRows.value.some((r) => isSelected(r)) && !allOnPageSelected.value,\n)\n\nfunction toggleAll() {\n if (allOnPageSelected.value) {\n selected.value = selected.value.filter(\n (r) => !visibleRows.value.some((v) => rowId(v) === rowId(r)),\n )\n } else {\n selected.value = [...selected.value, ...visibleRows.value.filter((r) => !isSelected(r))]\n }\n}\n\n// ── Helpers ────────────────────────────────────────────────────────────────\nconst extraCols = computed(\n () => (props.selectable ? 1 : 0) + (useSlots()['row-actions'] ? 1 : 0),\n)\n\nfunction alignClass(align?: string) {\n return align === 'center' ? 'text-center' : align === 'right' ? 'text-right' : 'text-left'\n}\nfunction skelWidth(ri: number, ci: number) {\n return `${SKEL[(ri * 3 + ci) % SKEL.length]}%`\n}\n\nimport { useSlots } from 'vue'\nconst slots = useSlots()\nconst hasActions = computed(() => !!slots['row-actions'])\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n\n <!-- ── Toolbar ───────────────────────────────────────────────────────── -->\n <div\n v-if=\"searchable || $slots.toolbar\"\n class=\"flex flex-wrap items-center gap-3 border-b border-outline-variant bg-surface-container-lowest px-4 py-2.5\"\n >\n <!-- Search -->\n <div v-if=\"searchable\" class=\"flex min-w-48 flex-1 items-center gap-2 rounded-full border border-outline-variant bg-surface-container px-3 py-1.5 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30 transition-[border-color,box-shadow] duration-150\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n v-model=\"search\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\"\n />\n <button\n v-if=\"search\"\n class=\"text-on-surface-variant transition-colors hover:text-on-surface\"\n @click=\"search = ''\"\n >\n <MIcon name=\"close\" :size=\"14\" />\n </button>\n </div>\n\n <!-- Extra toolbar content (filters, buttons, etc.) -->\n <slot name=\"toolbar\" />\n\n <!-- Selection count pill -->\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 scale-90\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 scale-90\"\n >\n <span\n v-if=\"selectable && selected.length > 0\"\n class=\"rounded-full bg-primary/12 px-3 py-1 text-label-small font-medium text-primary\"\n >\n {{ selected.length }} seleccionado{{ selected.length !== 1 ? 's' : '' }}\n </span>\n </Transition>\n </div>\n\n <!-- ── Table ─────────────────────────────────────────────────────────── -->\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n\n <!-- Header -->\n <thead>\n <tr class=\"bg-surface-container-high\">\n <th v-if=\"selectable\" class=\"w-12 px-4 py-3\">\n <MCheckbox\n :model-value=\"allOnPageSelected\"\n :indeterminate=\"someOnPageSelected\"\n @update:model-value=\"toggleAll\"\n />\n </th>\n <th\n v-for=\"col in columns\"\n :key=\"col.key\"\n :style=\"col.width ? { width: col.width } : undefined\"\n :class=\"[\n 'px-4 py-3 text-label-medium font-medium text-on-surface-variant whitespace-nowrap',\n alignClass(col.align),\n col.sortable\n ? 'cursor-pointer select-none hover:text-on-surface transition-colors duration-100'\n : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon\n v-if=\"sortKey === col.key && sortDir === 'asc'\"\n name=\"arrow_upward\"\n :size=\"14\"\n class=\"text-primary\"\n />\n <MIcon\n v-else-if=\"sortKey === col.key && sortDir === 'desc'\"\n name=\"arrow_downward\"\n :size=\"14\"\n class=\"text-primary\"\n />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n </th>\n <th v-if=\"hasActions\" class=\"w-1 px-4 py-3\" />\n </tr>\n </thead>\n\n <!-- Body -->\n <tbody>\n <!-- Loading skeleton -->\n <template v-if=\"loading\">\n <tr\n v-for=\"ri in perPage\"\n :key=\"`sk-${ri}`\"\n class=\"border-t border-outline-variant\"\n >\n <td v-if=\"selectable\" class=\"px-4 py-3.5\">\n <div class=\"h-4 w-4 animate-pulse rounded bg-on-surface/10\" />\n </td>\n <td\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n class=\"px-4 py-3.5\"\n >\n <div\n class=\"h-4 animate-pulse rounded-full bg-on-surface/10\"\n :style=\"{ width: skelWidth(ri, ci) }\"\n />\n </td>\n <td v-if=\"hasActions\" class=\"px-4 py-3.5\">\n <div class=\"ml-auto h-4 w-16 animate-pulse rounded-full bg-on-surface/10\" />\n </td>\n </tr>\n </template>\n\n <!-- Empty state -->\n <template v-else-if=\"visibleRows.length === 0\">\n <tr>\n <td\n :colspan=\"columns.length + extraCols\"\n class=\"border-t border-outline-variant px-4 py-14 text-center\"\n >\n <slot name=\"empty\">\n <MIcon name=\"search_off\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ emptyText }}</p>\n </slot>\n </td>\n </tr>\n </template>\n\n <!-- Data rows -->\n <template v-else>\n <tr\n v-for=\"row in visibleRows\"\n :key=\"rowId(row)\"\n :class=\"[\n 'border-t border-outline-variant transition-colors duration-100',\n 'hover:bg-on-surface/[0.04]',\n selectable && isSelected(row) ? 'bg-primary/[0.06]' : '',\n selectable ? 'cursor-pointer' : '',\n ]\"\n @click=\"selectable ? toggleRow(row) : undefined\"\n >\n <td v-if=\"selectable\" class=\"px-4 py-3\" @click.stop=\"toggleRow(row)\">\n <MCheckbox\n :model-value=\"isSelected(row)\"\n @update:model-value=\"toggleRow(row)\"\n />\n </td>\n <td\n v-for=\"col in columns\"\n :key=\"col.key\"\n :class=\"['px-4 py-3 text-body-medium text-on-surface', alignClass(col.align)]\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\" :col=\"col\">\n {{ row[col.key] ?? '—' }}\n </slot>\n </td>\n <td v-if=\"hasActions\" class=\"px-4 py-3 text-right\" @click.stop>\n <slot name=\"row-actions\" :row=\"row\" />\n </td>\n </tr>\n </template>\n </tbody>\n\n </table>\n </div>\n\n <!-- ── Footer ────────────────────────────────────────────────────────── -->\n <div class=\"border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <MPagination\n :page=\"currentPage\"\n :per-page=\"perPage\"\n :total=\"totalCount\"\n @update:page=\"currentPage = $event\"\n />\n </div>\n\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport MPagination from './MPagination.vue'\n\nexport interface TableColumn {\n key: string\n label: string\n sortable?: boolean\n width?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport interface TableFetchParams {\n page: number\n perPage: number\n search: string\n sortKey: string\n sortDir: 'asc' | 'desc' | ''\n}\n\n// Static widths so skeleton markup is stable across re-renders\nconst SKEL = [65, 80, 50, 75, 90, 55, 70, 85, 60, 78, 88, 52, 70, 83, 58]\n\nconst props = withDefaults(\n defineProps<{\n columns: TableColumn[]\n rows: Record<string, any>[]\n loading?: boolean\n emptyText?: string\n rowKey?: string\n selectable?: boolean\n modelValue?: Record<string, any>[]\n perPage?: number\n searchable?: boolean\n /** Emit `fetch` instead of filtering locally. Requires :total. */\n serverSide?: boolean\n total?: number\n page?: number\n }>(),\n {\n loading: false,\n emptyText: 'Sin resultados',\n rowKey: 'id',\n selectable: false,\n modelValue: () => [],\n perPage: 10,\n searchable: true,\n serverSide: false,\n total: 0,\n page: 1,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [Record<string, any>[]]\n 'update:page': [number]\n fetch: [TableFetchParams]\n}>()\n\n// ── Search ─────────────────────────────────────────────────────────────────\nconst search = ref('')\n\n// ── Sort ───────────────────────────────────────────────────────────────────\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) {\n sortKey.value = key\n sortDir.value = 'asc'\n } else if (sortDir.value === 'asc') {\n sortDir.value = 'desc'\n } else {\n sortKey.value = ''\n sortDir.value = ''\n }\n}\n\n// ── Pagination ─────────────────────────────────────────────────────────────\nconst internalPage = ref(1)\n\nconst currentPage = computed({\n get: () => (props.serverSide ? (props.page ?? 1) : internalPage.value),\n set: (val: number) => {\n if (props.serverSide) emit('update:page', val)\n else internalPage.value = val\n },\n})\n\n// ── Client-side data processing ────────────────────────────────────────────\nconst processedRows = computed(() => {\n if (props.serverSide) return props.rows\n\n let result = props.rows\n\n if (search.value.trim()) {\n const q = search.value.toLowerCase()\n result = result.filter((row) =>\n props.columns.some((col) => {\n const val = row[col.key]\n return val != null && String(val).toLowerCase().includes(q)\n }),\n )\n }\n\n if (sortKey.value && sortDir.value) {\n const key = sortKey.value\n const dir = sortDir.value\n result = [...result].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, {\n numeric: true,\n sensitivity: 'base',\n })\n return dir === 'asc' ? cmp : -cmp\n })\n }\n\n return result\n})\n\nconst totalCount = computed(() =>\n props.serverSide ? (props.total ?? 0) : processedRows.value.length,\n)\n\nconst visibleRows = computed(() => {\n if (props.serverSide) return props.rows\n const start = (currentPage.value - 1) * props.perPage\n return processedRows.value.slice(start, start + props.perPage)\n})\n\nwatch([search, sortKey, sortDir], () => {\n if (!props.serverSide) internalPage.value = 1\n})\n\n// ── Server-side fetch ──────────────────────────────────────────────────────\nconst mounted = ref(false)\n\nfunction emitFetch() {\n emit('fetch', {\n page: currentPage.value,\n perPage: props.perPage,\n search: search.value,\n sortKey: sortKey.value,\n sortDir: sortDir.value,\n })\n}\n\nonMounted(() => {\n mounted.value = true\n if (props.serverSide) emitFetch()\n})\n\nwatch([search, sortKey, sortDir], () => {\n if (!props.serverSide || !mounted.value) return\n internalPage.value = 1\n emitFetch()\n})\n\nwatch(currentPage, () => {\n if (!props.serverSide || !mounted.value) return\n emitFetch()\n})\n\n// ── Row selection ──────────────────────────────────────────────────────────\nconst selected = computed({\n get: () => props.modelValue ?? [],\n set: (val) => emit('update:modelValue', val),\n})\n\nfunction rowId(row: Record<string, any>) {\n return row[props.rowKey]\n}\nfunction isSelected(row: Record<string, any>) {\n return selected.value.some((r) => rowId(r) === rowId(row))\n}\nfunction toggleRow(row: Record<string, any>) {\n if (isSelected(row)) selected.value = selected.value.filter((r) => rowId(r) !== rowId(row))\n else selected.value = [...selected.value, row]\n}\n\nconst allOnPageSelected = computed(\n () => visibleRows.value.length > 0 && visibleRows.value.every((r) => isSelected(r)),\n)\nconst someOnPageSelected = computed(\n () => visibleRows.value.some((r) => isSelected(r)) && !allOnPageSelected.value,\n)\n\nfunction toggleAll() {\n if (allOnPageSelected.value) {\n selected.value = selected.value.filter(\n (r) => !visibleRows.value.some((v) => rowId(v) === rowId(r)),\n )\n } else {\n selected.value = [...selected.value, ...visibleRows.value.filter((r) => !isSelected(r))]\n }\n}\n\n// ── Helpers ────────────────────────────────────────────────────────────────\nconst extraCols = computed(\n () => (props.selectable ? 1 : 0) + (useSlots()['row-actions'] ? 1 : 0),\n)\n\nfunction alignClass(align?: string) {\n return align === 'center' ? 'text-center' : align === 'right' ? 'text-right' : 'text-left'\n}\nfunction skelWidth(ri: number, ci: number) {\n return `${SKEL[(ri * 3 + ci) % SKEL.length]}%`\n}\n\nimport { useSlots } from 'vue'\nconst slots = useSlots()\nconst hasActions = computed(() => !!slots['row-actions'])\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n\n <!-- ── Toolbar ───────────────────────────────────────────────────────── -->\n <div\n v-if=\"searchable || $slots.toolbar\"\n class=\"flex flex-wrap items-center gap-3 border-b border-outline-variant bg-surface-container-lowest px-4 py-2.5\"\n >\n <!-- Search -->\n <div v-if=\"searchable\" class=\"flex min-w-48 flex-1 items-center gap-2 rounded-full border border-outline-variant bg-surface-container px-3 py-1.5 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30 transition-[border-color,box-shadow] duration-150\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n v-model=\"search\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\"\n />\n <button\n v-if=\"search\"\n class=\"text-on-surface-variant transition-colors hover:text-on-surface\"\n @click=\"search = ''\"\n >\n <MIcon name=\"close\" :size=\"14\" />\n </button>\n </div>\n\n <!-- Extra toolbar content (filters, buttons, etc.) -->\n <slot name=\"toolbar\" />\n\n <!-- Selection count pill -->\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 scale-90\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 scale-90\"\n >\n <span\n v-if=\"selectable && selected.length > 0\"\n class=\"rounded-full bg-primary/12 px-3 py-1 text-label-small font-medium text-primary\"\n >\n {{ selected.length }} seleccionado{{ selected.length !== 1 ? 's' : '' }}\n </span>\n </Transition>\n </div>\n\n <!-- ── Table ─────────────────────────────────────────────────────────── -->\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n\n <!-- Header -->\n <thead>\n <tr class=\"bg-surface-container-high\">\n <th v-if=\"selectable\" class=\"w-12 px-4 py-3\">\n <MCheckbox\n :model-value=\"allOnPageSelected\"\n :indeterminate=\"someOnPageSelected\"\n @update:model-value=\"toggleAll\"\n />\n </th>\n <th\n v-for=\"col in columns\"\n :key=\"col.key\"\n :style=\"col.width ? { width: col.width } : undefined\"\n :class=\"[\n 'px-4 py-3 text-label-medium font-medium text-on-surface-variant whitespace-nowrap',\n alignClass(col.align),\n col.sortable\n ? 'cursor-pointer select-none hover:text-on-surface transition-colors duration-100'\n : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon\n v-if=\"sortKey === col.key && sortDir === 'asc'\"\n name=\"arrow_upward\"\n :size=\"14\"\n class=\"text-primary\"\n />\n <MIcon\n v-else-if=\"sortKey === col.key && sortDir === 'desc'\"\n name=\"arrow_downward\"\n :size=\"14\"\n class=\"text-primary\"\n />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n </th>\n <th v-if=\"hasActions\" class=\"w-1 px-4 py-3\" />\n </tr>\n </thead>\n\n <!-- Body -->\n <tbody>\n <!-- Loading skeleton -->\n <template v-if=\"loading\">\n <tr\n v-for=\"ri in perPage\"\n :key=\"`sk-${ri}`\"\n class=\"border-t border-outline-variant\"\n >\n <td v-if=\"selectable\" class=\"px-4 py-3.5\">\n <div class=\"h-4 w-4 animate-pulse rounded bg-on-surface/10\" />\n </td>\n <td\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n class=\"px-4 py-3.5\"\n >\n <div\n class=\"h-4 animate-pulse rounded-full bg-on-surface/10\"\n :style=\"{ width: skelWidth(ri, ci) }\"\n />\n </td>\n <td v-if=\"hasActions\" class=\"px-4 py-3.5\">\n <div class=\"ml-auto h-4 w-16 animate-pulse rounded-full bg-on-surface/10\" />\n </td>\n </tr>\n </template>\n\n <!-- Empty state -->\n <template v-else-if=\"visibleRows.length === 0\">\n <tr>\n <td\n :colspan=\"columns.length + extraCols\"\n class=\"border-t border-outline-variant px-4 py-14 text-center\"\n >\n <slot name=\"empty\">\n <MIcon name=\"search_off\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ emptyText }}</p>\n </slot>\n </td>\n </tr>\n </template>\n\n <!-- Data rows -->\n <template v-else>\n <tr\n v-for=\"row in visibleRows\"\n :key=\"rowId(row)\"\n :class=\"[\n 'border-t border-outline-variant transition-colors duration-100',\n 'hover:bg-on-surface/[0.04]',\n selectable && isSelected(row) ? 'bg-primary/[0.06]' : '',\n selectable ? 'cursor-pointer' : '',\n ]\"\n @click=\"selectable ? toggleRow(row) : undefined\"\n >\n <td v-if=\"selectable\" class=\"px-4 py-3\" @click.stop=\"toggleRow(row)\">\n <MCheckbox\n :model-value=\"isSelected(row)\"\n @update:model-value=\"toggleRow(row)\"\n />\n </td>\n <td\n v-for=\"col in columns\"\n :key=\"col.key\"\n :class=\"['px-4 py-3 text-body-medium text-on-surface', alignClass(col.align)]\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\" :col=\"col\">\n {{ row[col.key] ?? '—' }}\n </slot>\n </td>\n <td v-if=\"hasActions\" class=\"px-4 py-3 text-right\" @click.stop>\n <slot name=\"row-actions\" :row=\"row\" />\n </td>\n </tr>\n </template>\n </tbody>\n\n </table>\n </div>\n\n <!-- ── Footer ────────────────────────────────────────────────────────── -->\n <div class=\"border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <MPagination\n :page=\"currentPage\"\n :per-page=\"perPage\"\n :total=\"totalCount\"\n @update:page=\"currentPage = $event\"\n />\n </div>\n\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\n\ninterface Tab {\n value: string | number\n label: string\n icon?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: string | number\n tabs: Tab[]\n variant?: 'primary' | 'secondary'\n}>(), { variant: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number] }>()\n\n// Primary tab: refs for indicator position\nconst tabEls = ref<HTMLElement[]>([])\nconst indicatorLeft = ref(0)\nconst indicatorWidth = ref(0)\n\nfunction updateIndicator() {\n nextTick(() => {\n const idx = props.tabs.findIndex((t) => t.value === props.modelValue)\n const el = tabEls.value[idx]\n if (!el) return\n indicatorLeft.value = el.offsetLeft\n indicatorWidth.value = el.offsetWidth\n })\n}\n\nonMounted(updateIndicator)\nwatch(() => props.modelValue, updateIndicator)\nwatch(() => props.tabs, updateIndicator, { deep: true })\n\nfunction select(tab: Tab) {\n if (!tab.disabled) emit('update:modelValue', tab.value)\n}\n</script>\n\n<template>\n <!-- ── Primary: underline with sliding indicator ──────────────────────── -->\n <div v-if=\"variant === 'primary'\" class=\"relative border-b border-outline-variant\">\n <div class=\"flex overflow-x-auto\" style=\"scrollbar-width: none\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.value\"\n :ref=\"(el) => { if (el) tabEls[tabs.indexOf(tab)] = el as HTMLElement }\"\n type=\"button\"\n class=\"relative flex h-12 shrink-0 flex-col items-center justify-center gap-0.5 px-6 text-label-large transition-colors duration-150 focus-visible:outline-none\"\n :class=\"[\n tab.value === modelValue\n ? 'text-primary'\n : tab.disabled\n ? 'cursor-not-allowed text-on-surface/38'\n : 'cursor-pointer text-on-surface-variant hover:text-on-surface',\n tab.icon ? 'pt-2 pb-1' : '',\n ]\"\n :disabled=\"tab.disabled\"\n @click=\"select(tab)\"\n >\n <MIcon v-if=\"tab.icon\" :name=\"tab.icon\" :size=\"20\" />\n <span>{{ tab.label }}</span>\n </button>\n </div>\n <!-- Sliding indicator -->\n <div\n class=\"absolute bottom-0 h-[3px] rounded-t-sm bg-primary transition-[left,width] duration-200 ease-[cubic-bezier(0.2,0,0,1)]\"\n :style=\"{ left: `${indicatorLeft}px`, width: `${indicatorWidth}px` }\"\n />\n </div>\n\n <!-- ── Secondary: pill style ──────────────────────────────────────────── -->\n <div v-else class=\"flex flex-wrap gap-1 rounded-full bg-surface-container p-1\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.value\"\n type=\"button\"\n class=\"flex h-8 items-center gap-2 rounded-full px-4 text-label-large transition-all duration-150 focus-visible:outline-none\"\n :class=\"\n tab.value === modelValue\n ? 'bg-secondary-container text-on-secondary-container shadow-elevation-1'\n : tab.disabled\n ? 'cursor-not-allowed text-on-surface/38'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8 hover:text-on-surface'\n \"\n :disabled=\"tab.disabled\"\n @click=\"select(tab)\"\n >\n <MIcon v-if=\"tab.icon\" :name=\"tab.icon\" :size=\"16\" />\n {{ tab.label }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\n\ninterface Tab {\n value: string | number\n label: string\n icon?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: string | number\n tabs: Tab[]\n variant?: 'primary' | 'secondary'\n}>(), { variant: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number] }>()\n\n// Primary tab: refs for indicator position\nconst tabEls = ref<HTMLElement[]>([])\nconst indicatorLeft = ref(0)\nconst indicatorWidth = ref(0)\n\nfunction updateIndicator() {\n nextTick(() => {\n const idx = props.tabs.findIndex((t) => t.value === props.modelValue)\n const el = tabEls.value[idx]\n if (!el) return\n indicatorLeft.value = el.offsetLeft\n indicatorWidth.value = el.offsetWidth\n })\n}\n\nonMounted(updateIndicator)\nwatch(() => props.modelValue, updateIndicator)\nwatch(() => props.tabs, updateIndicator, { deep: true })\n\nfunction select(tab: Tab) {\n if (!tab.disabled) emit('update:modelValue', tab.value)\n}\n</script>\n\n<template>\n <!-- ── Primary: underline with sliding indicator ──────────────────────── -->\n <div v-if=\"variant === 'primary'\" class=\"relative border-b border-outline-variant\">\n <div class=\"flex overflow-x-auto\" style=\"scrollbar-width: none\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.value\"\n :ref=\"(el) => { if (el) tabEls[tabs.indexOf(tab)] = el as HTMLElement }\"\n type=\"button\"\n class=\"relative flex h-12 shrink-0 flex-col items-center justify-center gap-0.5 px-6 text-label-large transition-colors duration-150 focus-visible:outline-none\"\n :class=\"[\n tab.value === modelValue\n ? 'text-primary'\n : tab.disabled\n ? 'cursor-not-allowed text-on-surface/38'\n : 'cursor-pointer text-on-surface-variant hover:text-on-surface',\n tab.icon ? 'pt-2 pb-1' : '',\n ]\"\n :disabled=\"tab.disabled\"\n @click=\"select(tab)\"\n >\n <MIcon v-if=\"tab.icon\" :name=\"tab.icon\" :size=\"20\" />\n <span>{{ tab.label }}</span>\n </button>\n </div>\n <!-- Sliding indicator -->\n <div\n class=\"absolute bottom-0 h-[3px] rounded-t-sm bg-primary transition-[left,width] duration-200 ease-[cubic-bezier(0.2,0,0,1)]\"\n :style=\"{ left: `${indicatorLeft}px`, width: `${indicatorWidth}px` }\"\n />\n </div>\n\n <!-- ── Secondary: pill style ──────────────────────────────────────────── -->\n <div v-else class=\"flex flex-wrap gap-1 rounded-full bg-surface-container p-1\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.value\"\n type=\"button\"\n class=\"flex h-8 items-center gap-2 rounded-full px-4 text-label-large transition-all duration-150 focus-visible:outline-none\"\n :class=\"\n tab.value === modelValue\n ? 'bg-secondary-container text-on-secondary-container shadow-elevation-1'\n : tab.disabled\n ? 'cursor-not-allowed text-on-surface/38'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8 hover:text-on-surface'\n \"\n :disabled=\"tab.disabled\"\n @click=\"select(tab)\"\n >\n <MIcon v-if=\"tab.icon\" :name=\"tab.icon\" :size=\"16\" />\n {{ tab.label }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, useSlots } from \"vue\";\nimport MIcon from \"./MIcon.vue\";\nimport { useFieldBg } from \"../composables/useFieldBg\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number;\n label: string;\n type?: string;\n variant?: \"filled\" | \"outlined\";\n error?: string;\n hint?: string;\n disabled?: boolean;\n required?: boolean;\n multiline?: boolean;\n rows?: number;\n autocomplete?: string;\n leadingIcon?: string;\n /**\n * Background color behind the label in outlined variant.\n * Defaults to the page surface color. Pass e.g. 'var(--color-surface-container-low)'\n * when the input is inside a card.\n */\n fieldBg?: string;\n }>(),\n {\n type: \"text\",\n variant: \"filled\",\n rows: 3,\n },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [string] }>();\n\nconst id = useId();\nconst slots = useSlots();\n\nconst fieldBgEl = ref<HTMLElement | null>(null);\nconst { resolvedFieldBg } = useFieldBg(fieldBgEl, () => props.fieldBg);\n\nconst inputClasses = computed(() => {\n const hasTrailing = !!slots.trailing;\n const pl = props.leadingIcon ? \"pl-12\" : \"pl-4\";\n const pr = hasTrailing ? \"pr-12\" : \"pr-4\";\n const size = props.multiline ? \"resize-y min-h-[56px]\" : \"h-14\";\n const base = [\n \"peer block w-full text-body-large text-on-surface outline-none placeholder:text-transparent\",\n \"transition-[border-color,border-width] duration-150\",\n \"disabled:cursor-not-allowed disabled:opacity-[0.38]\",\n size,\n pl,\n pr,\n ];\n\n if (props.variant === \"outlined\") {\n return [\n ...base,\n \"rounded-sm border bg-transparent py-4\",\n props.error\n ? \"border-error focus:border-2 focus:border-error\"\n : \"border-outline hover:border-on-surface focus:border-2 focus:border-primary\",\n ].join(\" \");\n }\n\n return [\n ...base,\n \"rounded-t-sm bg-surface-container-highest border-b pt-6 pb-2\",\n props.error\n ? \"border-error focus:border-b-2 focus:border-error\"\n : \"border-on-surface-variant hover:border-on-surface focus:border-b-2 focus:border-primary\",\n ].join(\" \");\n});\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? props.variant === \"outlined\"\n ? \"left-11\"\n : \"left-12\"\n : props.variant === \"outlined\"\n ? \"left-3\"\n : \"left-4\";\n\n const base = [\n \"pointer-events-none absolute truncate transition-all duration-200\",\n left,\n \"right-4\",\n \"top-1/2 -translate-y-1/2 text-body-large\",\n ];\n\n if (props.variant === \"outlined\") {\n // When floated: drop right-4 (right-auto) and cap max-width so the label\n // shrinks to its own text width. The bg then only covers the glyphs + px-1,\n // cutting the border just where the text sits instead of a long strip.\n return [\n ...base,\n \"peer-focus:-top-2.5 peer-focus:translate-y-0 peer-focus:text-label-small peer-focus:right-auto peer-focus:max-w-[calc(100%-1.5rem)] peer-focus:bg-[var(--field-bg)] peer-focus:px-1\",\n \"peer-[&:not(:placeholder-shown)]:-top-2.5 peer-[&:not(:placeholder-shown)]:translate-y-0 peer-[&:not(:placeholder-shown)]:right-auto peer-[&:not(:placeholder-shown)]:max-w-[calc(100%-1.5rem)]\",\n \"peer-[&:not(:placeholder-shown)]:text-label-small peer-[&:not(:placeholder-shown)]:bg-[var(--field-bg)] peer-[&:not(:placeholder-shown)]:px-1\",\n props.error\n ? \"text-error peer-focus:text-error\"\n : \"text-on-surface-variant peer-focus:text-primary\",\n ].join(\" \");\n }\n\n // Filled: label floats to top-2 (slightly higher than before)\n return [\n ...base,\n \"peer-focus:top-2 peer-focus:translate-y-0 peer-focus:text-label-small\",\n \"peer-[&:not(:placeholder-shown)]:top-2 peer-[&:not(:placeholder-shown)]:translate-y-0 peer-[&:not(:placeholder-shown)]:text-label-small\",\n props.error\n ? \"text-error peer-focus:text-error\"\n : \"text-on-surface-variant peer-focus:text-primary\",\n ].join(\" \");\n});\n\nfunction onInput(event: Event) {\n const target = event.target as HTMLInputElement | HTMLTextAreaElement;\n emit(\"update:modelValue\", target.value);\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <!--\n --field-bg: background behind the floating label in outlined mode, so it\n \"cuts through\" the border. Auto-detected from the nearest opaque ancestor\n (see resolveBg); overridable via the fieldBg prop; falls back to surface.\n -->\n <div\n ref=\"fieldBgEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <!-- Leading icon: centered in the 48px left zone (left-3.5 → center at 24px) -->\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <textarea\n v-if=\"multiline\"\n :id=\"id\"\n :value=\"String(modelValue)\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :required=\"required\"\n placeholder=\" \"\n :class=\"inputClasses\"\n @input=\"onInput\"\n />\n <input\n v-else\n :id=\"id\"\n :type=\"type\"\n :value=\"modelValue\"\n :disabled=\"disabled\"\n :required=\"required\"\n :autocomplete=\"autocomplete\"\n placeholder=\" \"\n :class=\"inputClasses\"\n @input=\"onInput\"\n />\n\n <label :for=\"id\" :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <div v-if=\"$slots.trailing\" class=\"absolute top-1/2 right-2 -translate-y-1/2\">\n <slot name=\"trailing\" />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, useSlots } from \"vue\";\nimport MIcon from \"./MIcon.vue\";\nimport { useFieldBg } from \"../composables/useFieldBg\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number;\n label: string;\n type?: string;\n variant?: \"filled\" | \"outlined\";\n error?: string;\n hint?: string;\n disabled?: boolean;\n required?: boolean;\n multiline?: boolean;\n rows?: number;\n autocomplete?: string;\n leadingIcon?: string;\n /**\n * Background color behind the label in outlined variant.\n * Defaults to the page surface color. Pass e.g. 'var(--color-surface-container-low)'\n * when the input is inside a card.\n */\n fieldBg?: string;\n }>(),\n {\n type: \"text\",\n variant: \"filled\",\n rows: 3,\n },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [string] }>();\n\nconst id = useId();\nconst slots = useSlots();\n\nconst fieldBgEl = ref<HTMLElement | null>(null);\nconst { resolvedFieldBg } = useFieldBg(fieldBgEl, () => props.fieldBg);\n\nconst inputClasses = computed(() => {\n const hasTrailing = !!slots.trailing;\n const pl = props.leadingIcon ? \"pl-12\" : \"pl-4\";\n const pr = hasTrailing ? \"pr-12\" : \"pr-4\";\n const size = props.multiline ? \"resize-y min-h-[56px]\" : \"h-14\";\n const base = [\n \"peer block w-full text-body-large text-on-surface outline-none placeholder:text-transparent\",\n \"transition-[border-color,border-width] duration-150\",\n \"disabled:cursor-not-allowed disabled:opacity-[0.38]\",\n size,\n pl,\n pr,\n ];\n\n if (props.variant === \"outlined\") {\n return [\n ...base,\n \"rounded-sm border bg-transparent py-4\",\n props.error\n ? \"border-error focus:border-2 focus:border-error\"\n : \"border-outline hover:border-on-surface focus:border-2 focus:border-primary\",\n ].join(\" \");\n }\n\n return [\n ...base,\n \"rounded-t-sm bg-surface-container-highest border-b pt-6 pb-2\",\n props.error\n ? \"border-error focus:border-b-2 focus:border-error\"\n : \"border-on-surface-variant hover:border-on-surface focus:border-b-2 focus:border-primary\",\n ].join(\" \");\n});\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? props.variant === \"outlined\"\n ? \"left-11\"\n : \"left-12\"\n : props.variant === \"outlined\"\n ? \"left-3\"\n : \"left-4\";\n\n const base = [\n \"pointer-events-none absolute truncate transition-all duration-200\",\n left,\n \"right-4\",\n \"top-1/2 -translate-y-1/2 text-body-large\",\n ];\n\n if (props.variant === \"outlined\") {\n // When floated: drop right-4 (right-auto) and cap max-width so the label\n // shrinks to its own text width. The bg then only covers the glyphs + px-1,\n // cutting the border just where the text sits instead of a long strip.\n return [\n ...base,\n \"peer-focus:-top-2.5 peer-focus:translate-y-0 peer-focus:text-label-small peer-focus:right-auto peer-focus:max-w-[calc(100%-1.5rem)] peer-focus:bg-[var(--field-bg)] peer-focus:px-1\",\n \"peer-[&:not(:placeholder-shown)]:-top-2.5 peer-[&:not(:placeholder-shown)]:translate-y-0 peer-[&:not(:placeholder-shown)]:right-auto peer-[&:not(:placeholder-shown)]:max-w-[calc(100%-1.5rem)]\",\n \"peer-[&:not(:placeholder-shown)]:text-label-small peer-[&:not(:placeholder-shown)]:bg-[var(--field-bg)] peer-[&:not(:placeholder-shown)]:px-1\",\n props.error\n ? \"text-error peer-focus:text-error\"\n : \"text-on-surface-variant peer-focus:text-primary\",\n ].join(\" \");\n }\n\n // Filled: label floats to top-2 (slightly higher than before)\n return [\n ...base,\n \"peer-focus:top-2 peer-focus:translate-y-0 peer-focus:text-label-small\",\n \"peer-[&:not(:placeholder-shown)]:top-2 peer-[&:not(:placeholder-shown)]:translate-y-0 peer-[&:not(:placeholder-shown)]:text-label-small\",\n props.error\n ? \"text-error peer-focus:text-error\"\n : \"text-on-surface-variant peer-focus:text-primary\",\n ].join(\" \");\n});\n\nfunction onInput(event: Event) {\n const target = event.target as HTMLInputElement | HTMLTextAreaElement;\n emit(\"update:modelValue\", target.value);\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <!--\n --field-bg: background behind the floating label in outlined mode, so it\n \"cuts through\" the border. Auto-detected from the nearest opaque ancestor\n (see resolveBg); overridable via the fieldBg prop; falls back to surface.\n -->\n <div\n ref=\"fieldBgEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <!-- Leading icon: centered in the 48px left zone (left-3.5 → center at 24px) -->\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <textarea\n v-if=\"multiline\"\n :id=\"id\"\n :value=\"String(modelValue)\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :required=\"required\"\n placeholder=\" \"\n :class=\"inputClasses\"\n @input=\"onInput\"\n />\n <input\n v-else\n :id=\"id\"\n :type=\"type\"\n :value=\"modelValue\"\n :disabled=\"disabled\"\n :required=\"required\"\n :autocomplete=\"autocomplete\"\n placeholder=\" \"\n :class=\"inputClasses\"\n @input=\"onInput\"\n />\n\n <label :for=\"id\" :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <div v-if=\"$slots.trailing\" class=\"absolute top-1/2 right-2 -translate-y-1/2\">\n <slot name=\"trailing\" />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from \"./MIcon.vue\";\n\nexport interface TimelineItem {\n title: string;\n description?: string;\n date?: string;\n icon?: string;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\" | \"success\";\n dotColor?: string;\n}\n\nwithDefaults(\n defineProps<{\n items: TimelineItem[];\n dense?: boolean;\n alternating?: boolean;\n }>(),\n { dense: false, alternating: false },\n);\n\nconst dotBg: Record<string, string> = {\n primary: \"bg-primary text-on-primary\",\n secondary: \"bg-secondary text-on-secondary\",\n tertiary: \"bg-tertiary text-on-tertiary\",\n error: \"bg-error text-on-error\",\n success: \"bg-success text-on-success\",\n};\n</script>\n\n<template>\n <div :class=\"alternating ? 'relative' : 'flex flex-col'\">\n <!-- Standard layout -->\n <template v-if=\"!alternating\">\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n class=\"relative flex gap-4\"\n :class=\"dense ? 'pb-4' : 'pb-8'\"\n >\n <!-- Line + dot -->\n <div class=\"flex flex-col items-center\">\n <div\n class=\"z-[1] flex shrink-0 items-center justify-center rounded-full\"\n :class=\"[item.icon ? 'h-9 w-9' : 'h-3 w-3', dotBg[item.color ?? 'primary']]\"\n :style=\"item.dotColor ? { backgroundColor: item.dotColor } : undefined\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n </div>\n <div\n v-if=\"i < items.length - 1\"\n class=\"w-[2px] flex-1\"\n :class=\"dotBg[item.color ?? 'primary']!.split(' ')[0] + '/30'\"\n style=\"min-height: 16px\"\n />\n </div>\n\n <!-- Content -->\n <div :class=\"item.icon ? '' : 'pt-0'\" class=\"-mt-0.5 flex-1\">\n <div class=\"flex items-baseline justify-between gap-2\">\n <p class=\"text-body-large font-medium text-on-surface\">{{ item.title }}</p>\n <span v-if=\"item.date\" class=\"shrink-0 text-label-small text-on-surface-variant\">{{\n item.date\n }}</span>\n </div>\n <p v-if=\"item.description\" class=\"mt-1 text-body-medium text-on-surface-variant\">\n {{ item.description }}\n </p>\n <div v-if=\"$slots[`item-${i}`]\" class=\"mt-2\">\n <slot :name=\"`item-${i}`\" :item=\"item\" />\n </div>\n </div>\n </div>\n </template>\n\n <!-- Alternating layout -->\n <template v-else>\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n class=\"flex items-stretch\"\n :class=\"i % 2 === 0 ? 'flex-row' : 'flex-row-reverse'\"\n >\n <!-- Content side -->\n <div\n class=\"flex-1\"\n :class=\"[i % 2 === 0 ? 'text-right' : 'text-left', dense ? 'pb-4' : 'pb-8']\"\n >\n <p class=\"text-body-large font-medium text-on-surface\">{{ item.title }}</p>\n <p v-if=\"item.description\" class=\"mt-1 text-body-medium text-on-surface-variant\">\n {{ item.description }}\n </p>\n <span\n v-if=\"item.date\"\n class=\"mt-1 inline-block text-label-small text-on-surface-variant\"\n >{{ item.date }}</span\n >\n </div>\n\n <!-- Center column: dot + continuous line -->\n <div class=\"flex w-14 shrink-0 flex-col items-center\">\n <div\n class=\"z-[1] flex shrink-0 items-center justify-center rounded-full\"\n :class=\"[item.icon ? 'h-9 w-9' : 'h-3.5 w-3.5', dotBg[item.color ?? 'primary']]\"\n :style=\"item.dotColor ? { backgroundColor: item.dotColor } : undefined\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n </div>\n <div v-if=\"i < items.length - 1\" class=\"w-[2px] flex-1 bg-outline-variant\" />\n </div>\n\n <!-- Empty side -->\n <div class=\"flex-1\" />\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from \"./MIcon.vue\";\n\nexport interface TimelineItem {\n title: string;\n description?: string;\n date?: string;\n icon?: string;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\" | \"success\";\n dotColor?: string;\n}\n\nwithDefaults(\n defineProps<{\n items: TimelineItem[];\n dense?: boolean;\n alternating?: boolean;\n }>(),\n { dense: false, alternating: false },\n);\n\nconst dotBg: Record<string, string> = {\n primary: \"bg-primary text-on-primary\",\n secondary: \"bg-secondary text-on-secondary\",\n tertiary: \"bg-tertiary text-on-tertiary\",\n error: \"bg-error text-on-error\",\n success: \"bg-success text-on-success\",\n};\n</script>\n\n<template>\n <div :class=\"alternating ? 'relative' : 'flex flex-col'\">\n <!-- Standard layout -->\n <template v-if=\"!alternating\">\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n class=\"relative flex gap-4\"\n :class=\"dense ? 'pb-4' : 'pb-8'\"\n >\n <!-- Line + dot -->\n <div class=\"flex flex-col items-center\">\n <div\n class=\"z-[1] flex shrink-0 items-center justify-center rounded-full\"\n :class=\"[item.icon ? 'h-9 w-9' : 'h-3 w-3', dotBg[item.color ?? 'primary']]\"\n :style=\"item.dotColor ? { backgroundColor: item.dotColor } : undefined\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n </div>\n <div\n v-if=\"i < items.length - 1\"\n class=\"w-[2px] flex-1\"\n :class=\"dotBg[item.color ?? 'primary']!.split(' ')[0] + '/30'\"\n style=\"min-height: 16px\"\n />\n </div>\n\n <!-- Content -->\n <div :class=\"item.icon ? '' : 'pt-0'\" class=\"-mt-0.5 flex-1\">\n <div class=\"flex items-baseline justify-between gap-2\">\n <p class=\"text-body-large font-medium text-on-surface\">{{ item.title }}</p>\n <span v-if=\"item.date\" class=\"shrink-0 text-label-small text-on-surface-variant\">{{\n item.date\n }}</span>\n </div>\n <p v-if=\"item.description\" class=\"mt-1 text-body-medium text-on-surface-variant\">\n {{ item.description }}\n </p>\n <div v-if=\"$slots[`item-${i}`]\" class=\"mt-2\">\n <slot :name=\"`item-${i}`\" :item=\"item\" />\n </div>\n </div>\n </div>\n </template>\n\n <!-- Alternating layout -->\n <template v-else>\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n class=\"flex items-stretch\"\n :class=\"i % 2 === 0 ? 'flex-row' : 'flex-row-reverse'\"\n >\n <!-- Content side -->\n <div\n class=\"flex-1\"\n :class=\"[i % 2 === 0 ? 'text-right' : 'text-left', dense ? 'pb-4' : 'pb-8']\"\n >\n <p class=\"text-body-large font-medium text-on-surface\">{{ item.title }}</p>\n <p v-if=\"item.description\" class=\"mt-1 text-body-medium text-on-surface-variant\">\n {{ item.description }}\n </p>\n <span\n v-if=\"item.date\"\n class=\"mt-1 inline-block text-label-small text-on-surface-variant\"\n >{{ item.date }}</span\n >\n </div>\n\n <!-- Center column: dot + continuous line -->\n <div class=\"flex w-14 shrink-0 flex-col items-center\">\n <div\n class=\"z-[1] flex shrink-0 items-center justify-center rounded-full\"\n :class=\"[item.icon ? 'h-9 w-9' : 'h-3.5 w-3.5', dotBg[item.color ?? 'primary']]\"\n :style=\"item.dotColor ? { backgroundColor: item.dotColor } : undefined\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n </div>\n <div v-if=\"i < items.length - 1\" class=\"w-[2px] flex-1 bg-outline-variant\" />\n </div>\n\n <!-- Empty side -->\n <div class=\"flex-1\" />\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string | null\n label?: string\n disabled?: boolean\n error?: string\n hint?: string\n minuteStep?: number\n use24h?: boolean\n fieldBg?: string\n}>(), { minuteStep: 5, use24h: true })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | null] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst mode = ref<'hour' | 'minute'>('hour')\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst parsed = computed(() => {\n if (!props.modelValue) return { h: 12, m: 0 }\n const parts = props.modelValue.split(':').map(Number)\n return { h: parts[0] ?? 12, m: parts[1] ?? 0 }\n})\n\nconst selectedHour = ref(parsed.value.h)\nconst selectedMinute = ref(parsed.value.m)\nwatch(() => props.modelValue, () => {\n selectedHour.value = parsed.value.h\n selectedMinute.value = parsed.value.m\n})\n\nconst hours = Array.from({ length: 24 }, (_, i) => i)\nconst minutes = computed(() => {\n const arr: number[] = []\n for (let m = 0; m < 60; m += props.minuteStep) arr.push(m)\n return arr\n})\n\nfunction pad(n: number) { return String(n).padStart(2, '0') }\n\nfunction selectHour(h: number) {\n selectedHour.value = h\n mode.value = 'minute'\n}\n\nfunction selectMinute(m: number) {\n selectedMinute.value = m\n emit('update:modelValue', `${pad(selectedHour.value)}:${pad(m)}`)\n open.value = false\n mode.value = 'hour'\n}\n\nfunction clear() {\n emit('update:modelValue', null)\n}\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n return `${pad(parsed.value.h)}:${pad(parsed.value.m)}`\n})\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 320\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n mode.value = 'hour'\n selectedHour.value = parsed.value.h\n selectedMinute.value = parsed.value.m\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"schedule\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 font-mono text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ label || 'Seleccionar hora' }}</span>\n <MIcon\n v-if=\"modelValue\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[280px] rounded-lg bg-surface-container shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Display -->\n <div class=\"flex items-center justify-center gap-1 border-b border-outline-variant px-4 py-4\">\n <button\n type=\"button\"\n class=\"rounded-lg px-3 py-2 font-mono text-headline-medium transition-colors\"\n :class=\"mode === 'hour' ? 'bg-primary-container text-on-primary-container cursor-default' : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"mode = 'hour'\"\n >\n {{ pad(selectedHour) }}\n </button>\n <span class=\"text-headline-medium text-on-surface-variant\">:</span>\n <button\n type=\"button\"\n class=\"rounded-lg px-3 py-2 font-mono text-headline-medium transition-colors\"\n :class=\"mode === 'minute' ? 'bg-primary-container text-on-primary-container cursor-default' : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"mode = 'minute'\"\n >\n {{ pad(selectedMinute) }}\n </button>\n </div>\n\n <!-- Grid -->\n <div class=\"p-3\">\n <div v-if=\"mode === 'hour'\" class=\"grid grid-cols-6 gap-1\">\n <button\n v-for=\"h in hours\"\n :key=\"h\"\n type=\"button\"\n class=\"flex h-9 cursor-pointer items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"\n h === selectedHour\n ? 'bg-primary text-on-primary'\n : 'text-on-surface hover:bg-on-surface/8'\n \"\n @click=\"selectHour(h)\"\n >\n {{ pad(h) }}\n </button>\n </div>\n\n <div v-else class=\"grid grid-cols-6 gap-1\">\n <button\n v-for=\"m in minutes\"\n :key=\"m\"\n type=\"button\"\n class=\"flex h-9 cursor-pointer items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"\n m === selectedMinute\n ? 'bg-primary text-on-primary'\n : 'text-on-surface hover:bg-on-surface/8'\n \"\n @click=\"selectMinute(m)\"\n >\n {{ pad(m) }}\n </button>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string | null\n label?: string\n disabled?: boolean\n error?: string\n hint?: string\n minuteStep?: number\n use24h?: boolean\n fieldBg?: string\n}>(), { minuteStep: 5, use24h: true })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | null] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst mode = ref<'hour' | 'minute'>('hour')\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst parsed = computed(() => {\n if (!props.modelValue) return { h: 12, m: 0 }\n const parts = props.modelValue.split(':').map(Number)\n return { h: parts[0] ?? 12, m: parts[1] ?? 0 }\n})\n\nconst selectedHour = ref(parsed.value.h)\nconst selectedMinute = ref(parsed.value.m)\nwatch(() => props.modelValue, () => {\n selectedHour.value = parsed.value.h\n selectedMinute.value = parsed.value.m\n})\n\nconst hours = Array.from({ length: 24 }, (_, i) => i)\nconst minutes = computed(() => {\n const arr: number[] = []\n for (let m = 0; m < 60; m += props.minuteStep) arr.push(m)\n return arr\n})\n\nfunction pad(n: number) { return String(n).padStart(2, '0') }\n\nfunction selectHour(h: number) {\n selectedHour.value = h\n mode.value = 'minute'\n}\n\nfunction selectMinute(m: number) {\n selectedMinute.value = m\n emit('update:modelValue', `${pad(selectedHour.value)}:${pad(m)}`)\n open.value = false\n mode.value = 'hour'\n}\n\nfunction clear() {\n emit('update:modelValue', null)\n}\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n return `${pad(parsed.value.h)}:${pad(parsed.value.m)}`\n})\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 320\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n mode.value = 'hour'\n selectedHour.value = parsed.value.h\n selectedMinute.value = parsed.value.m\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"schedule\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 font-mono text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ label || 'Seleccionar hora' }}</span>\n <MIcon\n v-if=\"modelValue\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[280px] rounded-lg bg-surface-container shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Display -->\n <div class=\"flex items-center justify-center gap-1 border-b border-outline-variant px-4 py-4\">\n <button\n type=\"button\"\n class=\"rounded-lg px-3 py-2 font-mono text-headline-medium transition-colors\"\n :class=\"mode === 'hour' ? 'bg-primary-container text-on-primary-container cursor-default' : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"mode = 'hour'\"\n >\n {{ pad(selectedHour) }}\n </button>\n <span class=\"text-headline-medium text-on-surface-variant\">:</span>\n <button\n type=\"button\"\n class=\"rounded-lg px-3 py-2 font-mono text-headline-medium transition-colors\"\n :class=\"mode === 'minute' ? 'bg-primary-container text-on-primary-container cursor-default' : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"mode = 'minute'\"\n >\n {{ pad(selectedMinute) }}\n </button>\n </div>\n\n <!-- Grid -->\n <div class=\"p-3\">\n <div v-if=\"mode === 'hour'\" class=\"grid grid-cols-6 gap-1\">\n <button\n v-for=\"h in hours\"\n :key=\"h\"\n type=\"button\"\n class=\"flex h-9 cursor-pointer items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"\n h === selectedHour\n ? 'bg-primary text-on-primary'\n : 'text-on-surface hover:bg-on-surface/8'\n \"\n @click=\"selectHour(h)\"\n >\n {{ pad(h) }}\n </button>\n </div>\n\n <div v-else class=\"grid grid-cols-6 gap-1\">\n <button\n v-for=\"m in minutes\"\n :key=\"m\"\n type=\"button\"\n class=\"flex h-9 cursor-pointer items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"\n m === selectedMinute\n ? 'bg-primary text-on-primary'\n : 'text-on-surface hover:bg-on-surface/8'\n \"\n @click=\"selectMinute(m)\"\n >\n {{ pad(m) }}\n </button>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, onUnmounted, ref } from 'vue'\n\nconst props = withDefaults(defineProps<{\n text: string\n placement?: 'top' | 'bottom' | 'left' | 'right'\n delay?: number\n}>(), { placement: 'top', delay: 600 })\n\nconst visible = ref(false)\nconst tipEl = ref<HTMLElement>()\nconst triggerEl = ref<HTMLElement>()\nconst tipStyle = ref<Record<string, string>>({})\nlet timer: ReturnType<typeof setTimeout> | null = null\n\nasync function show() {\n if (timer) clearTimeout(timer)\n timer = setTimeout(async () => {\n visible.value = true\n await nextTick()\n reposition()\n }, props.delay)\n}\n\nfunction hide() {\n if (timer) { clearTimeout(timer); timer = null }\n visible.value = false\n}\n\nfunction onScroll() {\n if (visible.value) hide()\n}\n\nfunction reposition() {\n if (!triggerEl.value || !tipEl.value) return\n const tr = triggerEl.value.getBoundingClientRect()\n const tt = tipEl.value.getBoundingClientRect()\n const GAP = 6\n\n let top = 0, left = 0\n switch (props.placement) {\n case 'top': top = tr.top - tt.height - GAP; left = tr.left + (tr.width - tt.width) / 2; break\n case 'bottom': top = tr.bottom + GAP; left = tr.left + (tr.width - tt.width) / 2; break\n case 'left': top = tr.top + (tr.height - tt.height) / 2; left = tr.left - tt.width - GAP; break\n case 'right': top = tr.top + (tr.height - tt.height) / 2; left = tr.right + GAP; break\n }\n\n top = Math.max(6, Math.min(top, window.innerHeight - tt.height - 6))\n left = Math.max(6, Math.min(left, window.innerWidth - tt.width - 6))\n tipStyle.value = { top: `${top}px`, left: `${left}px` }\n}\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => window.removeEventListener('scroll', onScroll, true))\n</script>\n\n<template>\n <span\n ref=\"triggerEl\"\n class=\"inline-flex\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n >\n <slot />\n </span>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-150\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-100\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible && text\"\n ref=\"tipEl\"\n class=\"pointer-events-none fixed z-[400] max-w-[220px] rounded bg-inverse-surface px-3 py-1.5 text-label-medium text-inverse-on-surface shadow-elevation-2\"\n :style=\"tipStyle\"\n role=\"tooltip\"\n >\n {{ text }}\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, onUnmounted, ref } from 'vue'\n\nconst props = withDefaults(defineProps<{\n text: string\n placement?: 'top' | 'bottom' | 'left' | 'right'\n delay?: number\n}>(), { placement: 'top', delay: 600 })\n\nconst visible = ref(false)\nconst tipEl = ref<HTMLElement>()\nconst triggerEl = ref<HTMLElement>()\nconst tipStyle = ref<Record<string, string>>({})\nlet timer: ReturnType<typeof setTimeout> | null = null\n\nasync function show() {\n if (timer) clearTimeout(timer)\n timer = setTimeout(async () => {\n visible.value = true\n await nextTick()\n reposition()\n }, props.delay)\n}\n\nfunction hide() {\n if (timer) { clearTimeout(timer); timer = null }\n visible.value = false\n}\n\nfunction onScroll() {\n if (visible.value) hide()\n}\n\nfunction reposition() {\n if (!triggerEl.value || !tipEl.value) return\n const tr = triggerEl.value.getBoundingClientRect()\n const tt = tipEl.value.getBoundingClientRect()\n const GAP = 6\n\n let top = 0, left = 0\n switch (props.placement) {\n case 'top': top = tr.top - tt.height - GAP; left = tr.left + (tr.width - tt.width) / 2; break\n case 'bottom': top = tr.bottom + GAP; left = tr.left + (tr.width - tt.width) / 2; break\n case 'left': top = tr.top + (tr.height - tt.height) / 2; left = tr.left - tt.width - GAP; break\n case 'right': top = tr.top + (tr.height - tt.height) / 2; left = tr.right + GAP; break\n }\n\n top = Math.max(6, Math.min(top, window.innerHeight - tt.height - 6))\n left = Math.max(6, Math.min(left, window.innerWidth - tt.width - 6))\n tipStyle.value = { top: `${top}px`, left: `${left}px` }\n}\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => window.removeEventListener('scroll', onScroll, true))\n</script>\n\n<template>\n <span\n ref=\"triggerEl\"\n class=\"inline-flex\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n >\n <slot />\n </span>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-150\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-100\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible && text\"\n ref=\"tipEl\"\n class=\"pointer-events-none fixed z-[400] max-w-[220px] rounded bg-inverse-surface px-3 py-1.5 text-label-medium text-inverse-on-surface shadow-elevation-2\"\n :style=\"tipStyle\"\n role=\"tooltip\"\n >\n {{ text }}\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nwithDefaults(defineProps<{\n title?: string\n variant?: 'center' | 'small' | 'medium' | 'large'\n navigationIcon?: string\n elevated?: boolean\n}>(), { variant: 'small' })\n\ndefineEmits<{ navigation: [] }>()\n</script>\n\n<template>\n <header\n class=\"flex w-full flex-col bg-surface transition-shadow\"\n :class=\"elevated ? 'shadow-elevation-2' : ''\"\n >\n <!-- Top row -->\n <div class=\"flex h-16 items-center gap-1 px-2\">\n <!-- Navigation icon -->\n <MIconButton\n v-if=\"navigationIcon\"\n :icon=\"navigationIcon\"\n label=\"Navegación\"\n @click=\"$emit('navigation')\"\n />\n\n <!-- Title: center or small variant -->\n <h1\n v-if=\"variant === 'center' || variant === 'small'\"\n class=\"flex-1 truncate px-2 text-title-large text-on-surface\"\n :class=\"variant === 'center' ? 'text-center' : ''\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n\n <!-- Spacer for medium/large (title is below) -->\n <div v-else class=\"flex-1\" />\n\n <!-- Trailing actions -->\n <div v-if=\"$slots.actions\" class=\"flex items-center gap-1\">\n <slot name=\"actions\" />\n </div>\n </div>\n\n <!-- Large title row for medium/large variants -->\n <div\n v-if=\"variant === 'medium' || variant === 'large'\"\n class=\"px-4 pb-6\"\n :class=\"variant === 'large' ? 'pt-4' : 'pt-1'\"\n >\n <h1\n class=\"text-on-surface\"\n :class=\"variant === 'large' ? 'text-headline-medium' : 'text-headline-small'\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nwithDefaults(defineProps<{\n title?: string\n variant?: 'center' | 'small' | 'medium' | 'large'\n navigationIcon?: string\n elevated?: boolean\n}>(), { variant: 'small' })\n\ndefineEmits<{ navigation: [] }>()\n</script>\n\n<template>\n <header\n class=\"flex w-full flex-col bg-surface transition-shadow\"\n :class=\"elevated ? 'shadow-elevation-2' : ''\"\n >\n <!-- Top row -->\n <div class=\"flex h-16 items-center gap-1 px-2\">\n <!-- Navigation icon -->\n <MIconButton\n v-if=\"navigationIcon\"\n :icon=\"navigationIcon\"\n label=\"Navegación\"\n @click=\"$emit('navigation')\"\n />\n\n <!-- Title: center or small variant -->\n <h1\n v-if=\"variant === 'center' || variant === 'small'\"\n class=\"flex-1 truncate px-2 text-title-large text-on-surface\"\n :class=\"variant === 'center' ? 'text-center' : ''\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n\n <!-- Spacer for medium/large (title is below) -->\n <div v-else class=\"flex-1\" />\n\n <!-- Trailing actions -->\n <div v-if=\"$slots.actions\" class=\"flex items-center gap-1\">\n <slot name=\"actions\" />\n </div>\n </div>\n\n <!-- Large title row for medium/large variants -->\n <div\n v-if=\"variant === 'medium' || variant === 'large'\"\n class=\"px-4 pb-6\"\n :class=\"variant === 'large' ? 'pt-4' : 'pt-1'\"\n >\n <h1\n class=\"text-on-surface\"\n :class=\"variant === 'large' ? 'text-headline-medium' : 'text-headline-small'\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onBeforeUnmount } from 'vue'\nimport MButton from './MButton.vue'\nimport MIcon from './MIcon.vue'\n\nexport interface TourStep {\n target: string\n title: string\n content: string\n placement?: 'top' | 'bottom' | 'left' | 'right'\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n steps: TourStep[]\n }>(),\n {},\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n finish: []\n}>()\n\nconst currentStep = ref(0)\nconst tooltipStyle = ref<Record<string, string>>({})\nconst arrowStyle = ref<Record<string, string>>({})\nconst placement = ref<'top' | 'bottom' | 'left' | 'right'>('bottom')\n\nconst step = computed(() => props.steps[currentStep.value])\nconst isFirst = computed(() => currentStep.value === 0)\nconst isLast = computed(() => currentStep.value === props.steps.length - 1)\n\nfunction positionTooltip() {\n if (!step.value) return\n const el = document.querySelector(step.value.target) as HTMLElement | null\n if (!el) return\n\n const rect = el.getBoundingClientRect()\n const pad = 12\n const arrowSize = 8\n const p = step.value.placement ?? 'bottom'\n placement.value = p\n\n el.scrollIntoView({ behavior: 'smooth', block: 'center' })\n\n const s: Record<string, string> = { position: 'fixed' }\n const a: Record<string, string> = { position: 'absolute' }\n\n switch (p) {\n case 'bottom':\n s.top = `${rect.bottom + pad}px`\n s.left = `${rect.left + rect.width / 2}px`\n s.transform = 'translateX(-50%)'\n a.top = `-${arrowSize}px`\n a.left = '50%'\n a.transform = 'translateX(-50%)'\n a.borderBottom = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderLeft = `${arrowSize}px solid transparent`\n a.borderRight = `${arrowSize}px solid transparent`\n break\n case 'top':\n s.bottom = `${window.innerHeight - rect.top + pad}px`\n s.left = `${rect.left + rect.width / 2}px`\n s.transform = 'translateX(-50%)'\n a.bottom = `-${arrowSize}px`\n a.left = '50%'\n a.transform = 'translateX(-50%)'\n a.borderTop = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderLeft = `${arrowSize}px solid transparent`\n a.borderRight = `${arrowSize}px solid transparent`\n break\n case 'left':\n s.top = `${rect.top + rect.height / 2}px`\n s.right = `${window.innerWidth - rect.left + pad}px`\n s.transform = 'translateY(-50%)'\n a.top = '50%'\n a.right = `-${arrowSize}px`\n a.transform = 'translateY(-50%)'\n a.borderLeft = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderTop = `${arrowSize}px solid transparent`\n a.borderBottom = `${arrowSize}px solid transparent`\n break\n case 'right':\n s.top = `${rect.top + rect.height / 2}px`\n s.left = `${rect.right + pad}px`\n s.transform = 'translateY(-50%)'\n a.top = '50%'\n a.left = `-${arrowSize}px`\n a.transform = 'translateY(-50%)'\n a.borderRight = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderTop = `${arrowSize}px solid transparent`\n a.borderBottom = `${arrowSize}px solid transparent`\n break\n }\n\n tooltipStyle.value = s\n arrowStyle.value = a\n}\n\nfunction highlightTarget() {\n if (!step.value) return\n document.querySelectorAll('.m3-tour-highlight').forEach((el) => el.classList.remove('m3-tour-highlight'))\n const el = document.querySelector(step.value.target)\n el?.classList.add('m3-tour-highlight')\n}\n\nfunction clearHighlight() {\n document.querySelectorAll('.m3-tour-highlight').forEach((el) => el.classList.remove('m3-tour-highlight'))\n}\n\nfunction goNext() {\n if (isLast.value) {\n close()\n emit('finish')\n } else {\n currentStep.value++\n }\n}\n\nfunction goPrev() {\n if (!isFirst.value) currentStep.value--\n}\n\nfunction close() {\n clearHighlight()\n currentStep.value = 0\n emit('update:modelValue', false)\n}\n\nwatch([() => props.modelValue, currentStep], () => {\n if (props.modelValue) {\n nextTick(() => {\n highlightTarget()\n positionTooltip()\n })\n }\n})\n\nwatch(() => props.modelValue, (v) => {\n if (!v) clearHighlight()\n})\n\nonBeforeUnmount(clearHighlight)\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-tour\">\n <div v-if=\"modelValue && step\" class=\"fixed inset-0 z-[100]\">\n <!-- Overlay -->\n <div class=\"absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Tooltip -->\n <div\n class=\"z-[101] w-80 rounded-xl bg-surface-container-high p-5 shadow-elevation-3\"\n :style=\"tooltipStyle\"\n >\n <!-- Arrow -->\n <div class=\"h-0 w-0\" :style=\"arrowStyle\" />\n\n <!-- Step indicator -->\n <div class=\"mb-2 flex items-center justify-between\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ currentStep + 1 }} / {{ steps.length }}\n </span>\n <button\n type=\"button\"\n class=\"flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"16\" />\n </button>\n </div>\n\n <h3 class=\"mb-1 text-title-medium font-medium text-on-surface\">{{ step.title }}</h3>\n <p class=\"mb-4 text-body-medium text-on-surface-variant\">{{ step.content }}</p>\n\n <!-- Progress dots -->\n <div class=\"mb-4 flex justify-center gap-1.5\">\n <div\n v-for=\"(_, i) in steps\"\n :key=\"i\"\n class=\"h-1.5 rounded-full transition-all duration-200\"\n :class=\"i === currentStep ? 'w-6 bg-primary' : 'w-1.5 bg-outline-variant'\"\n />\n </div>\n\n <!-- Actions -->\n <div class=\"flex justify-between\">\n <MButton\n v-if=\"!isFirst\"\n variant=\"text\"\n @click=\"goPrev\"\n >\n Anterior\n </MButton>\n <span v-else />\n <MButton @click=\"goNext\">\n {{ isLast ? 'Finalizar' : 'Siguiente' }}\n </MButton>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style>\n.m3-tour-highlight {\n position: relative;\n z-index: 101 !important;\n box-shadow: 0 0 0 4px var(--color-primary), 0 0 0 9999px rgba(0, 0, 0, 0.4);\n border-radius: 8px;\n}\n\n.m3-tour-enter-active,\n.m3-tour-leave-active {\n transition: opacity 0.2s ease;\n}\n.m3-tour-enter-from,\n.m3-tour-leave-to {\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onBeforeUnmount } from 'vue'\nimport MButton from './MButton.vue'\nimport MIcon from './MIcon.vue'\n\nexport interface TourStep {\n target: string\n title: string\n content: string\n placement?: 'top' | 'bottom' | 'left' | 'right'\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n steps: TourStep[]\n }>(),\n {},\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n finish: []\n}>()\n\nconst currentStep = ref(0)\nconst tooltipStyle = ref<Record<string, string>>({})\nconst arrowStyle = ref<Record<string, string>>({})\nconst placement = ref<'top' | 'bottom' | 'left' | 'right'>('bottom')\n\nconst step = computed(() => props.steps[currentStep.value])\nconst isFirst = computed(() => currentStep.value === 0)\nconst isLast = computed(() => currentStep.value === props.steps.length - 1)\n\nfunction positionTooltip() {\n if (!step.value) return\n const el = document.querySelector(step.value.target) as HTMLElement | null\n if (!el) return\n\n const rect = el.getBoundingClientRect()\n const pad = 12\n const arrowSize = 8\n const p = step.value.placement ?? 'bottom'\n placement.value = p\n\n el.scrollIntoView({ behavior: 'smooth', block: 'center' })\n\n const s: Record<string, string> = { position: 'fixed' }\n const a: Record<string, string> = { position: 'absolute' }\n\n switch (p) {\n case 'bottom':\n s.top = `${rect.bottom + pad}px`\n s.left = `${rect.left + rect.width / 2}px`\n s.transform = 'translateX(-50%)'\n a.top = `-${arrowSize}px`\n a.left = '50%'\n a.transform = 'translateX(-50%)'\n a.borderBottom = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderLeft = `${arrowSize}px solid transparent`\n a.borderRight = `${arrowSize}px solid transparent`\n break\n case 'top':\n s.bottom = `${window.innerHeight - rect.top + pad}px`\n s.left = `${rect.left + rect.width / 2}px`\n s.transform = 'translateX(-50%)'\n a.bottom = `-${arrowSize}px`\n a.left = '50%'\n a.transform = 'translateX(-50%)'\n a.borderTop = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderLeft = `${arrowSize}px solid transparent`\n a.borderRight = `${arrowSize}px solid transparent`\n break\n case 'left':\n s.top = `${rect.top + rect.height / 2}px`\n s.right = `${window.innerWidth - rect.left + pad}px`\n s.transform = 'translateY(-50%)'\n a.top = '50%'\n a.right = `-${arrowSize}px`\n a.transform = 'translateY(-50%)'\n a.borderLeft = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderTop = `${arrowSize}px solid transparent`\n a.borderBottom = `${arrowSize}px solid transparent`\n break\n case 'right':\n s.top = `${rect.top + rect.height / 2}px`\n s.left = `${rect.right + pad}px`\n s.transform = 'translateY(-50%)'\n a.top = '50%'\n a.left = `-${arrowSize}px`\n a.transform = 'translateY(-50%)'\n a.borderRight = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderTop = `${arrowSize}px solid transparent`\n a.borderBottom = `${arrowSize}px solid transparent`\n break\n }\n\n tooltipStyle.value = s\n arrowStyle.value = a\n}\n\nfunction highlightTarget() {\n if (!step.value) return\n document.querySelectorAll('.m3-tour-highlight').forEach((el) => el.classList.remove('m3-tour-highlight'))\n const el = document.querySelector(step.value.target)\n el?.classList.add('m3-tour-highlight')\n}\n\nfunction clearHighlight() {\n document.querySelectorAll('.m3-tour-highlight').forEach((el) => el.classList.remove('m3-tour-highlight'))\n}\n\nfunction goNext() {\n if (isLast.value) {\n close()\n emit('finish')\n } else {\n currentStep.value++\n }\n}\n\nfunction goPrev() {\n if (!isFirst.value) currentStep.value--\n}\n\nfunction close() {\n clearHighlight()\n currentStep.value = 0\n emit('update:modelValue', false)\n}\n\nwatch([() => props.modelValue, currentStep], () => {\n if (props.modelValue) {\n nextTick(() => {\n highlightTarget()\n positionTooltip()\n })\n }\n})\n\nwatch(() => props.modelValue, (v) => {\n if (!v) clearHighlight()\n})\n\nonBeforeUnmount(clearHighlight)\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-tour\">\n <div v-if=\"modelValue && step\" class=\"fixed inset-0 z-[100]\">\n <!-- Overlay -->\n <div class=\"absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Tooltip -->\n <div\n class=\"z-[101] w-80 rounded-xl bg-surface-container-high p-5 shadow-elevation-3\"\n :style=\"tooltipStyle\"\n >\n <!-- Arrow -->\n <div class=\"h-0 w-0\" :style=\"arrowStyle\" />\n\n <!-- Step indicator -->\n <div class=\"mb-2 flex items-center justify-between\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ currentStep + 1 }} / {{ steps.length }}\n </span>\n <button\n type=\"button\"\n class=\"flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"16\" />\n </button>\n </div>\n\n <h3 class=\"mb-1 text-title-medium font-medium text-on-surface\">{{ step.title }}</h3>\n <p class=\"mb-4 text-body-medium text-on-surface-variant\">{{ step.content }}</p>\n\n <!-- Progress dots -->\n <div class=\"mb-4 flex justify-center gap-1.5\">\n <div\n v-for=\"(_, i) in steps\"\n :key=\"i\"\n class=\"h-1.5 rounded-full transition-all duration-200\"\n :class=\"i === currentStep ? 'w-6 bg-primary' : 'w-1.5 bg-outline-variant'\"\n />\n </div>\n\n <!-- Actions -->\n <div class=\"flex justify-between\">\n <MButton\n v-if=\"!isFirst\"\n variant=\"text\"\n @click=\"goPrev\"\n >\n Anterior\n </MButton>\n <span v-else />\n <MButton @click=\"goNext\">\n {{ isLast ? 'Finalizar' : 'Siguiente' }}\n </MButton>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style>\n.m3-tour-highlight {\n position: relative;\n z-index: 101 !important;\n box-shadow: 0 0 0 4px var(--color-primary), 0 0 0 9999px rgba(0, 0, 0, 0.4);\n border-radius: 8px;\n}\n\n.m3-tour-enter-active,\n.m3-tour-leave-active {\n transition: opacity 0.2s ease;\n}\n.m3-tour-enter-from,\n.m3-tour-leave-to {\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MCheckbox from './MCheckbox.vue'\n\nexport interface TransferItem {\n value: string | number\n label: string\n icon?: string\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: (string | number)[]\n items: TransferItem[]\n sourceTitle?: string\n targetTitle?: string\n filterable?: boolean\n}>(), { sourceTitle: 'Disponibles', targetTitle: 'Seleccionados', filterable: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [(string | number)[]] }>()\n\nconst checkedSource = ref<Set<string | number>>(new Set())\nconst checkedTarget = ref<Set<string | number>>(new Set())\nconst sourceSearch = ref('')\nconst targetSearch = ref('')\n\nconst sourceItems = computed(() => {\n const selected = new Set(props.modelValue)\n let list = props.items.filter(i => !selected.has(i.value))\n if (sourceSearch.value) {\n const q = sourceSearch.value.toLowerCase()\n list = list.filter(i => i.label.toLowerCase().includes(q))\n }\n return list\n})\n\nconst targetItems = computed(() => {\n const selected = new Set(props.modelValue)\n let list = props.items.filter(i => selected.has(i.value))\n if (targetSearch.value) {\n const q = targetSearch.value.toLowerCase()\n list = list.filter(i => i.label.toLowerCase().includes(q))\n }\n return list\n})\n\nfunction toggleSource(value: string | number) {\n const s = new Set(checkedSource.value)\n s.has(value) ? s.delete(value) : s.add(value)\n checkedSource.value = s\n}\nfunction toggleTarget(value: string | number) {\n const s = new Set(checkedTarget.value)\n s.has(value) ? s.delete(value) : s.add(value)\n checkedTarget.value = s\n}\n\nfunction moveRight() {\n const next = [...props.modelValue, ...checkedSource.value]\n emit('update:modelValue', next)\n checkedSource.value = new Set()\n}\nfunction moveLeft() {\n const remove = checkedTarget.value\n emit('update:modelValue', props.modelValue.filter(v => !remove.has(v)))\n checkedTarget.value = new Set()\n}\nfunction moveAllRight() {\n const all = sourceItems.value.map(i => i.value)\n emit('update:modelValue', [...props.modelValue, ...all])\n checkedSource.value = new Set()\n}\nfunction moveAllLeft() {\n const keep = targetItems.value.map(i => i.value)\n emit('update:modelValue', props.modelValue.filter(v => !new Set(keep).has(v)))\n checkedTarget.value = new Set()\n}\n</script>\n\n<template>\n <div class=\"flex items-stretch gap-2\">\n <!-- Source list -->\n <div class=\"flex min-w-0 flex-1 flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-3 py-2\">\n <span class=\"text-label-large font-medium text-on-surface\">{{ sourceTitle }}</span>\n <span class=\"text-label-small text-on-surface-variant\">{{ sourceItems.length }}</span>\n </div>\n <div v-if=\"filterable\" class=\"border-b border-outline-variant px-3 py-2\">\n <input\n v-model=\"sourceSearch\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n />\n </div>\n <div class=\"flex-1 overflow-y-auto\" style=\"max-height: 240px\">\n <button\n v-for=\"item in sourceItems\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-on-surface/4\"\n @click=\"toggleSource(item.value)\"\n >\n <MCheckbox :model-value=\"checkedSource.has(item.value)\" @update:model-value=\"toggleSource(item.value)\" />\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n <span class=\"flex-1 truncate text-body-medium text-on-surface\">{{ item.label }}</span>\n </button>\n <p v-if=\"!sourceItems.length\" class=\"px-3 py-4 text-center text-body-small text-on-surface-variant\">\n Sin elementos\n </p>\n </div>\n </div>\n\n <!-- Transfer buttons -->\n <div class=\"flex flex-col items-center justify-center gap-1\">\n <MIconButton\n icon=\"keyboard_double_arrow_right\"\n label=\"Mover todos a la derecha\"\n :size=\"36\"\n :disabled=\"!sourceItems.length\"\n @click=\"moveAllRight\"\n />\n <MIconButton\n icon=\"chevron_right\"\n label=\"Mover seleccionados a la derecha\"\n variant=\"tonal\"\n :size=\"36\"\n :disabled=\"!checkedSource.size\"\n @click=\"moveRight\"\n />\n <MIconButton\n icon=\"chevron_left\"\n label=\"Mover seleccionados a la izquierda\"\n variant=\"tonal\"\n :size=\"36\"\n :disabled=\"!checkedTarget.size\"\n @click=\"moveLeft\"\n />\n <MIconButton\n icon=\"keyboard_double_arrow_left\"\n label=\"Mover todos a la izquierda\"\n :size=\"36\"\n :disabled=\"!targetItems.length\"\n @click=\"moveAllLeft\"\n />\n </div>\n\n <!-- Target list -->\n <div class=\"flex min-w-0 flex-1 flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-3 py-2\">\n <span class=\"text-label-large font-medium text-on-surface\">{{ targetTitle }}</span>\n <span class=\"text-label-small text-on-surface-variant\">{{ targetItems.length }}</span>\n </div>\n <div v-if=\"filterable\" class=\"border-b border-outline-variant px-3 py-2\">\n <input\n v-model=\"targetSearch\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n />\n </div>\n <div class=\"flex-1 overflow-y-auto\" style=\"max-height: 240px\">\n <button\n v-for=\"item in targetItems\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-on-surface/4\"\n @click=\"toggleTarget(item.value)\"\n >\n <MCheckbox :model-value=\"checkedTarget.has(item.value)\" @update:model-value=\"toggleTarget(item.value)\" />\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n <span class=\"flex-1 truncate text-body-medium text-on-surface\">{{ item.label }}</span>\n </button>\n <p v-if=\"!targetItems.length\" class=\"px-3 py-4 text-center text-body-small text-on-surface-variant\">\n Sin elementos\n </p>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MCheckbox from './MCheckbox.vue'\n\nexport interface TransferItem {\n value: string | number\n label: string\n icon?: string\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: (string | number)[]\n items: TransferItem[]\n sourceTitle?: string\n targetTitle?: string\n filterable?: boolean\n}>(), { sourceTitle: 'Disponibles', targetTitle: 'Seleccionados', filterable: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [(string | number)[]] }>()\n\nconst checkedSource = ref<Set<string | number>>(new Set())\nconst checkedTarget = ref<Set<string | number>>(new Set())\nconst sourceSearch = ref('')\nconst targetSearch = ref('')\n\nconst sourceItems = computed(() => {\n const selected = new Set(props.modelValue)\n let list = props.items.filter(i => !selected.has(i.value))\n if (sourceSearch.value) {\n const q = sourceSearch.value.toLowerCase()\n list = list.filter(i => i.label.toLowerCase().includes(q))\n }\n return list\n})\n\nconst targetItems = computed(() => {\n const selected = new Set(props.modelValue)\n let list = props.items.filter(i => selected.has(i.value))\n if (targetSearch.value) {\n const q = targetSearch.value.toLowerCase()\n list = list.filter(i => i.label.toLowerCase().includes(q))\n }\n return list\n})\n\nfunction toggleSource(value: string | number) {\n const s = new Set(checkedSource.value)\n s.has(value) ? s.delete(value) : s.add(value)\n checkedSource.value = s\n}\nfunction toggleTarget(value: string | number) {\n const s = new Set(checkedTarget.value)\n s.has(value) ? s.delete(value) : s.add(value)\n checkedTarget.value = s\n}\n\nfunction moveRight() {\n const next = [...props.modelValue, ...checkedSource.value]\n emit('update:modelValue', next)\n checkedSource.value = new Set()\n}\nfunction moveLeft() {\n const remove = checkedTarget.value\n emit('update:modelValue', props.modelValue.filter(v => !remove.has(v)))\n checkedTarget.value = new Set()\n}\nfunction moveAllRight() {\n const all = sourceItems.value.map(i => i.value)\n emit('update:modelValue', [...props.modelValue, ...all])\n checkedSource.value = new Set()\n}\nfunction moveAllLeft() {\n const keep = targetItems.value.map(i => i.value)\n emit('update:modelValue', props.modelValue.filter(v => !new Set(keep).has(v)))\n checkedTarget.value = new Set()\n}\n</script>\n\n<template>\n <div class=\"flex items-stretch gap-2\">\n <!-- Source list -->\n <div class=\"flex min-w-0 flex-1 flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-3 py-2\">\n <span class=\"text-label-large font-medium text-on-surface\">{{ sourceTitle }}</span>\n <span class=\"text-label-small text-on-surface-variant\">{{ sourceItems.length }}</span>\n </div>\n <div v-if=\"filterable\" class=\"border-b border-outline-variant px-3 py-2\">\n <input\n v-model=\"sourceSearch\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n />\n </div>\n <div class=\"flex-1 overflow-y-auto\" style=\"max-height: 240px\">\n <button\n v-for=\"item in sourceItems\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-on-surface/4\"\n @click=\"toggleSource(item.value)\"\n >\n <MCheckbox :model-value=\"checkedSource.has(item.value)\" @update:model-value=\"toggleSource(item.value)\" />\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n <span class=\"flex-1 truncate text-body-medium text-on-surface\">{{ item.label }}</span>\n </button>\n <p v-if=\"!sourceItems.length\" class=\"px-3 py-4 text-center text-body-small text-on-surface-variant\">\n Sin elementos\n </p>\n </div>\n </div>\n\n <!-- Transfer buttons -->\n <div class=\"flex flex-col items-center justify-center gap-1\">\n <MIconButton\n icon=\"keyboard_double_arrow_right\"\n label=\"Mover todos a la derecha\"\n :size=\"36\"\n :disabled=\"!sourceItems.length\"\n @click=\"moveAllRight\"\n />\n <MIconButton\n icon=\"chevron_right\"\n label=\"Mover seleccionados a la derecha\"\n variant=\"tonal\"\n :size=\"36\"\n :disabled=\"!checkedSource.size\"\n @click=\"moveRight\"\n />\n <MIconButton\n icon=\"chevron_left\"\n label=\"Mover seleccionados a la izquierda\"\n variant=\"tonal\"\n :size=\"36\"\n :disabled=\"!checkedTarget.size\"\n @click=\"moveLeft\"\n />\n <MIconButton\n icon=\"keyboard_double_arrow_left\"\n label=\"Mover todos a la izquierda\"\n :size=\"36\"\n :disabled=\"!targetItems.length\"\n @click=\"moveAllLeft\"\n />\n </div>\n\n <!-- Target list -->\n <div class=\"flex min-w-0 flex-1 flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-3 py-2\">\n <span class=\"text-label-large font-medium text-on-surface\">{{ targetTitle }}</span>\n <span class=\"text-label-small text-on-surface-variant\">{{ targetItems.length }}</span>\n </div>\n <div v-if=\"filterable\" class=\"border-b border-outline-variant px-3 py-2\">\n <input\n v-model=\"targetSearch\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n />\n </div>\n <div class=\"flex-1 overflow-y-auto\" style=\"max-height: 240px\">\n <button\n v-for=\"item in targetItems\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-on-surface/4\"\n @click=\"toggleTarget(item.value)\"\n >\n <MCheckbox :model-value=\"checkedTarget.has(item.value)\" @update:model-value=\"toggleTarget(item.value)\" />\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n <span class=\"flex-1 truncate text-body-medium text-on-surface\">{{ item.label }}</span>\n </button>\n <p v-if=\"!targetItems.length\" class=\"px-3 py-4 text-center text-body-small text-on-surface-variant\">\n Sin elementos\n </p>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from 'vue'\nimport MTreeNode from './_MTreeNode.vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport type { TreeContext, TreeNode } from './MTree.vue'\n\nconst props = defineProps<{ node: TreeNode; depth: number }>()\n\nconst tree = inject<TreeContext>('m-tree')!\n\nconst hasChildren = computed(() => !!props.node.children?.length)\nconst isExpanded = computed(() => tree.expandedIds.value.has(props.node.id))\nconst isSelected = computed(() => tree.selected.value === props.node.id)\n\n// Use only leaf ids for checkbox visual state (branch ids are never stored in checkedSet)\nconst leafIds = computed(() => tree.getLeafIds(props.node))\nconst checkedLeafCount = computed(() => leafIds.value.filter(id => tree.checkedSet.value.has(id)).length)\nconst isChecked = computed(() => leafIds.value.length > 0 && checkedLeafCount.value === leafIds.value.length)\nconst isIndeterminate = computed(() => checkedLeafCount.value > 0 && !isChecked.value)\n\nfunction onRowClick() {\n if (props.node.disabled) return\n tree.selectNode(props.node)\n if (hasChildren.value) tree.toggleExpand(props.node.id)\n}\n\nfunction onChevronClick(e: MouseEvent) {\n e.stopPropagation()\n if (props.node.disabled) return\n tree.toggleExpand(props.node.id)\n}\n\nfunction onCheck() {\n if (props.node.disabled) return\n tree.toggleCheck(props.node)\n}\n\n// ── Height transition hooks ────────────────────────────────────────────────\nfunction onEnter(el: Element) {\n const e = el as HTMLElement\n e.style.height = '0'\n e.style.opacity = '0'\n e.style.overflow = 'hidden'\n e.offsetHeight // force reflow\n e.style.transition = 'height 200ms cubic-bezier(0.4,0,0.2,1), opacity 150ms ease'\n e.style.height = e.scrollHeight + 'px'\n e.style.opacity = '1'\n}\nfunction onAfterEnter(el: Element) {\n const e = el as HTMLElement\n e.style.height = ''\n e.style.overflow = ''\n e.style.transition = ''\n e.style.opacity = ''\n}\nfunction onLeave(el: Element) {\n const e = el as HTMLElement\n e.style.height = e.scrollHeight + 'px'\n e.style.overflow = 'hidden'\n e.offsetHeight // force reflow\n e.style.transition = 'height 180ms cubic-bezier(0.4,0,0.2,1), opacity 120ms ease'\n e.style.height = '0'\n e.style.opacity = '0'\n}\nfunction onAfterLeave(el: Element) {\n const e = el as HTMLElement\n e.style.height = ''\n e.style.overflow = ''\n e.style.transition = ''\n e.style.opacity = ''\n}\n</script>\n\n<template>\n <div role=\"treeitem\" :aria-expanded=\"hasChildren ? isExpanded : undefined\" :aria-selected=\"isSelected\">\n <!-- ── Row ─────────────────────────────────────────────────────────────── -->\n <div\n :class=\"[\n 'flex items-center gap-1 rounded-sm py-1 pr-2 transition-colors duration-100 select-none',\n node.disabled\n ? 'cursor-not-allowed opacity-38'\n : 'cursor-pointer',\n !node.disabled && isSelected\n ? 'bg-primary/[0.10]'\n : !node.disabled\n ? 'hover:bg-on-surface/[0.04]'\n : '',\n ]\"\n @click=\"onRowClick\"\n >\n <!-- Indent spacer (no lines for depth 0, lines provided by parent's border-l) -->\n <div class=\"flex w-6 shrink-0 items-center justify-center\">\n <!-- Chevron for branch nodes -->\n <button\n v-if=\"hasChildren\"\n type=\"button\"\n class=\"flex h-5 w-5 items-center justify-center rounded text-on-surface-variant transition-transform duration-200\"\n :class=\"isExpanded ? 'rotate-90' : ''\"\n :disabled=\"node.disabled || undefined\"\n @click=\"onChevronClick\"\n >\n <MIcon name=\"chevron_right\" :size=\"16\" />\n </button>\n </div>\n\n <!-- Checkbox (checkable mode) -->\n <div v-if=\"tree.checkable.value\" class=\"shrink-0\" @click.stop=\"onCheck\">\n <MCheckbox\n :model-value=\"isChecked\"\n :indeterminate=\"isIndeterminate\"\n :disabled=\"node.disabled\"\n @update:model-value=\"onCheck\"\n />\n </div>\n\n <!-- Node icon -->\n <MIcon\n v-if=\"node.icon\"\n :name=\"node.icon\"\n :size=\"16\"\n class=\"shrink-0 transition-colors\"\n :class=\"isSelected ? 'text-primary' : 'text-on-surface-variant'\"\n />\n\n <!-- Label -->\n <span\n class=\"min-w-0 flex-1 truncate text-body-medium transition-colors\"\n :class=\"isSelected ? 'font-medium text-primary' : 'text-on-surface'\"\n >\n <slot name=\"label\" :node=\"node\">{{ node.label }}</slot>\n </span>\n\n <!-- Check count badge (branch + checkable) -->\n <span\n v-if=\"hasChildren && tree.checkable.value\"\n class=\"shrink-0 text-label-small tabular-nums text-on-surface-variant\"\n >\n {{ checkedLeafCount }}/{{ leafIds.length }}\n </span>\n\n <!-- Optional trailing slot -->\n <slot name=\"trailing\" :node=\"node\" />\n </div>\n\n <!-- ── Children ───────────────────────────────────────────────────────── -->\n <Transition\n @enter=\"onEnter\"\n @after-enter=\"onAfterEnter\"\n @leave=\"onLeave\"\n @after-leave=\"onAfterLeave\"\n >\n <div\n v-if=\"isExpanded && hasChildren\"\n class=\"ml-3 border-l border-outline-variant pl-2\"\n >\n <MTreeNode\n v-for=\"child in node.children\"\n :key=\"child.id\"\n :node=\"child\"\n :depth=\"depth + 1\"\n >\n <!-- @vue-ignore -->\n <template v-for=\"(_, name) in $slots\" #[name]=\"sp\">\n <slot :name=\"name\" v-bind=\"sp ?? {}\" />\n </template>\n </MTreeNode>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from 'vue'\nimport MTreeNode from './_MTreeNode.vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport type { TreeContext, TreeNode } from './MTree.vue'\n\nconst props = defineProps<{ node: TreeNode; depth: number }>()\n\nconst tree = inject<TreeContext>('m-tree')!\n\nconst hasChildren = computed(() => !!props.node.children?.length)\nconst isExpanded = computed(() => tree.expandedIds.value.has(props.node.id))\nconst isSelected = computed(() => tree.selected.value === props.node.id)\n\n// Use only leaf ids for checkbox visual state (branch ids are never stored in checkedSet)\nconst leafIds = computed(() => tree.getLeafIds(props.node))\nconst checkedLeafCount = computed(() => leafIds.value.filter(id => tree.checkedSet.value.has(id)).length)\nconst isChecked = computed(() => leafIds.value.length > 0 && checkedLeafCount.value === leafIds.value.length)\nconst isIndeterminate = computed(() => checkedLeafCount.value > 0 && !isChecked.value)\n\nfunction onRowClick() {\n if (props.node.disabled) return\n tree.selectNode(props.node)\n if (hasChildren.value) tree.toggleExpand(props.node.id)\n}\n\nfunction onChevronClick(e: MouseEvent) {\n e.stopPropagation()\n if (props.node.disabled) return\n tree.toggleExpand(props.node.id)\n}\n\nfunction onCheck() {\n if (props.node.disabled) return\n tree.toggleCheck(props.node)\n}\n\n// ── Height transition hooks ────────────────────────────────────────────────\nfunction onEnter(el: Element) {\n const e = el as HTMLElement\n e.style.height = '0'\n e.style.opacity = '0'\n e.style.overflow = 'hidden'\n e.offsetHeight // force reflow\n e.style.transition = 'height 200ms cubic-bezier(0.4,0,0.2,1), opacity 150ms ease'\n e.style.height = e.scrollHeight + 'px'\n e.style.opacity = '1'\n}\nfunction onAfterEnter(el: Element) {\n const e = el as HTMLElement\n e.style.height = ''\n e.style.overflow = ''\n e.style.transition = ''\n e.style.opacity = ''\n}\nfunction onLeave(el: Element) {\n const e = el as HTMLElement\n e.style.height = e.scrollHeight + 'px'\n e.style.overflow = 'hidden'\n e.offsetHeight // force reflow\n e.style.transition = 'height 180ms cubic-bezier(0.4,0,0.2,1), opacity 120ms ease'\n e.style.height = '0'\n e.style.opacity = '0'\n}\nfunction onAfterLeave(el: Element) {\n const e = el as HTMLElement\n e.style.height = ''\n e.style.overflow = ''\n e.style.transition = ''\n e.style.opacity = ''\n}\n</script>\n\n<template>\n <div role=\"treeitem\" :aria-expanded=\"hasChildren ? isExpanded : undefined\" :aria-selected=\"isSelected\">\n <!-- ── Row ─────────────────────────────────────────────────────────────── -->\n <div\n :class=\"[\n 'flex items-center gap-1 rounded-sm py-1 pr-2 transition-colors duration-100 select-none',\n node.disabled\n ? 'cursor-not-allowed opacity-38'\n : 'cursor-pointer',\n !node.disabled && isSelected\n ? 'bg-primary/[0.10]'\n : !node.disabled\n ? 'hover:bg-on-surface/[0.04]'\n : '',\n ]\"\n @click=\"onRowClick\"\n >\n <!-- Indent spacer (no lines for depth 0, lines provided by parent's border-l) -->\n <div class=\"flex w-6 shrink-0 items-center justify-center\">\n <!-- Chevron for branch nodes -->\n <button\n v-if=\"hasChildren\"\n type=\"button\"\n class=\"flex h-5 w-5 items-center justify-center rounded text-on-surface-variant transition-transform duration-200\"\n :class=\"isExpanded ? 'rotate-90' : ''\"\n :disabled=\"node.disabled || undefined\"\n @click=\"onChevronClick\"\n >\n <MIcon name=\"chevron_right\" :size=\"16\" />\n </button>\n </div>\n\n <!-- Checkbox (checkable mode) -->\n <div v-if=\"tree.checkable.value\" class=\"shrink-0\" @click.stop=\"onCheck\">\n <MCheckbox\n :model-value=\"isChecked\"\n :indeterminate=\"isIndeterminate\"\n :disabled=\"node.disabled\"\n @update:model-value=\"onCheck\"\n />\n </div>\n\n <!-- Node icon -->\n <MIcon\n v-if=\"node.icon\"\n :name=\"node.icon\"\n :size=\"16\"\n class=\"shrink-0 transition-colors\"\n :class=\"isSelected ? 'text-primary' : 'text-on-surface-variant'\"\n />\n\n <!-- Label -->\n <span\n class=\"min-w-0 flex-1 truncate text-body-medium transition-colors\"\n :class=\"isSelected ? 'font-medium text-primary' : 'text-on-surface'\"\n >\n <slot name=\"label\" :node=\"node\">{{ node.label }}</slot>\n </span>\n\n <!-- Check count badge (branch + checkable) -->\n <span\n v-if=\"hasChildren && tree.checkable.value\"\n class=\"shrink-0 text-label-small tabular-nums text-on-surface-variant\"\n >\n {{ checkedLeafCount }}/{{ leafIds.length }}\n </span>\n\n <!-- Optional trailing slot -->\n <slot name=\"trailing\" :node=\"node\" />\n </div>\n\n <!-- ── Children ───────────────────────────────────────────────────────── -->\n <Transition\n @enter=\"onEnter\"\n @after-enter=\"onAfterEnter\"\n @leave=\"onLeave\"\n @after-leave=\"onAfterLeave\"\n >\n <div\n v-if=\"isExpanded && hasChildren\"\n class=\"ml-3 border-l border-outline-variant pl-2\"\n >\n <MTreeNode\n v-for=\"child in node.children\"\n :key=\"child.id\"\n :node=\"child\"\n :depth=\"depth + 1\"\n >\n <!-- @vue-ignore -->\n <template v-for=\"(_, name) in $slots\" #[name]=\"sp\">\n <slot :name=\"name\" v-bind=\"sp ?? {}\" />\n </template>\n </MTreeNode>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide, ref, type Ref } from 'vue'\nimport MTreeNode from './_MTreeNode.vue'\nimport MIcon from './MIcon.vue'\n\n// ── Public types ────────────────────────────────────────────────────────────\n\nexport interface TreeNode {\n id: string | number\n label: string\n icon?: string\n children?: TreeNode[]\n disabled?: boolean\n [key: string]: unknown\n}\n\n/** Shape injected into every _MTreeNode via provide/inject. */\nexport interface TreeContext {\n selected: Ref<string | number | null>\n checkedSet: Ref<Set<string | number>>\n expandedIds: Ref<Set<string | number>>\n checkable: Ref<boolean>\n selectNode: (node: TreeNode) => void\n toggleExpand: (id: string | number) => void\n toggleCheck: (node: TreeNode) => void\n getDescendantIds: (node: TreeNode) => (string | number)[]\n getLeafIds: (node: TreeNode) => (string | number)[]\n}\n\n// ── Props & emits ───────────────────────────────────────────────────────────\n\nconst props = withDefaults(\n defineProps<{\n nodes: TreeNode[]\n /** Currently selected node id (single-select). */\n selected?: string | number | null\n /** Checked node ids (checkable multi-select). */\n checked?: (string | number)[]\n /** Show checkboxes with cascade selection. */\n checkable?: boolean\n /**\n * Which nodes start expanded.\n * 'all' | 'none' | array of ids (default: 'none').\n */\n defaultExpanded?: (string | number)[] | 'all' | 'none'\n emptyText?: string\n }>(),\n {\n selected: null,\n checked: () => [],\n checkable: false,\n defaultExpanded: 'none',\n emptyText: 'Sin elementos',\n },\n)\n\nconst emit = defineEmits<{\n 'update:selected': [string | number | null]\n 'update:checked': [(string | number)[]]\n 'node-click': [TreeNode]\n}>()\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction getDescendantIds(node: TreeNode): (string | number)[] {\n return [node.id, ...(node.children ?? []).flatMap(getDescendantIds)]\n}\n\nfunction getLeafIds(node: TreeNode): (string | number)[] {\n if (!node.children?.length) return [node.id]\n return node.children.flatMap(getLeafIds)\n}\n\nfunction getAllIds(nodes: TreeNode[]): (string | number)[] {\n return nodes.flatMap((n) => getDescendantIds(n))\n}\n\n// ── Expand state ────────────────────────────────────────────────────────────\n\nfunction buildInitialExpanded(): Set<string | number> {\n if (props.defaultExpanded === 'all') return new Set(getAllIds(props.nodes))\n if (props.defaultExpanded === 'none') return new Set()\n return new Set(props.defaultExpanded)\n}\n\nconst expandedIds = ref<Set<string | number>>(buildInitialExpanded())\n\nfunction toggleExpand(id: string | number) {\n const next = new Set(expandedIds.value)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n expandedIds.value = next\n}\n\n// ── Selection ───────────────────────────────────────────────────────────────\n\nconst selectedRef = computed(() => props.selected ?? null)\n\nfunction selectNode(node: TreeNode) {\n emit('update:selected', selectedRef.value === node.id ? null : node.id)\n emit('node-click', node)\n}\n\n// ── Checkable ───────────────────────────────────────────────────────────────\n\nconst checkedSet = computed(() => new Set(props.checked))\n\nfunction toggleCheck(node: TreeNode) {\n const leafIds = getLeafIds(node)\n const allLeafsChecked = leafIds.every((id) => checkedSet.value.has(id))\n const next = new Set(props.checked)\n if (allLeafsChecked) {\n // Remove leaf ids + clean up any stale branch ids\n getDescendantIds(node).forEach((id) => next.delete(id))\n } else {\n leafIds.forEach((id) => next.add(id))\n }\n emit('update:checked', [...next])\n}\n\n// ── Provide context ─────────────────────────────────────────────────────────\n\nprovide<TreeContext>('m-tree', {\n selected: selectedRef,\n checkedSet,\n expandedIds,\n checkable: computed(() => props.checkable),\n selectNode,\n toggleExpand,\n toggleCheck,\n getDescendantIds,\n getLeafIds,\n})\n\n// ── Expose expand/collapse utilities ────────────────────────────────────────\n\nfunction expandAll() { expandedIds.value = new Set(getAllIds(props.nodes)) }\nfunction collapseAll() { expandedIds.value = new Set() }\n\ndefineExpose({ expandAll, collapseAll })\n</script>\n\n<template>\n <div role=\"tree\" class=\"flex flex-col\">\n <template v-if=\"nodes.length\">\n <MTreeNode\n v-for=\"node in nodes\"\n :key=\"node.id\"\n :node=\"node\"\n :depth=\"0\"\n >\n <!-- Forward all slots down the recursive tree -->\n <template v-for=\"(_, name) in $slots\" #[name]=\"slotProps\">\n <slot :name=\"name\" v-bind=\"slotProps ?? {}\" />\n </template>\n </MTreeNode>\n </template>\n\n <div v-else class=\"flex flex-col items-center gap-2 py-10 text-on-surface-variant\">\n <MIcon name=\"account_tree\" :size=\"32\" class=\"opacity-30\" />\n <p class=\"text-body-medium\">{{ emptyText }}</p>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide, ref, type Ref } from 'vue'\nimport MTreeNode from './_MTreeNode.vue'\nimport MIcon from './MIcon.vue'\n\n// ── Public types ────────────────────────────────────────────────────────────\n\nexport interface TreeNode {\n id: string | number\n label: string\n icon?: string\n children?: TreeNode[]\n disabled?: boolean\n [key: string]: unknown\n}\n\n/** Shape injected into every _MTreeNode via provide/inject. */\nexport interface TreeContext {\n selected: Ref<string | number | null>\n checkedSet: Ref<Set<string | number>>\n expandedIds: Ref<Set<string | number>>\n checkable: Ref<boolean>\n selectNode: (node: TreeNode) => void\n toggleExpand: (id: string | number) => void\n toggleCheck: (node: TreeNode) => void\n getDescendantIds: (node: TreeNode) => (string | number)[]\n getLeafIds: (node: TreeNode) => (string | number)[]\n}\n\n// ── Props & emits ───────────────────────────────────────────────────────────\n\nconst props = withDefaults(\n defineProps<{\n nodes: TreeNode[]\n /** Currently selected node id (single-select). */\n selected?: string | number | null\n /** Checked node ids (checkable multi-select). */\n checked?: (string | number)[]\n /** Show checkboxes with cascade selection. */\n checkable?: boolean\n /**\n * Which nodes start expanded.\n * 'all' | 'none' | array of ids (default: 'none').\n */\n defaultExpanded?: (string | number)[] | 'all' | 'none'\n emptyText?: string\n }>(),\n {\n selected: null,\n checked: () => [],\n checkable: false,\n defaultExpanded: 'none',\n emptyText: 'Sin elementos',\n },\n)\n\nconst emit = defineEmits<{\n 'update:selected': [string | number | null]\n 'update:checked': [(string | number)[]]\n 'node-click': [TreeNode]\n}>()\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction getDescendantIds(node: TreeNode): (string | number)[] {\n return [node.id, ...(node.children ?? []).flatMap(getDescendantIds)]\n}\n\nfunction getLeafIds(node: TreeNode): (string | number)[] {\n if (!node.children?.length) return [node.id]\n return node.children.flatMap(getLeafIds)\n}\n\nfunction getAllIds(nodes: TreeNode[]): (string | number)[] {\n return nodes.flatMap((n) => getDescendantIds(n))\n}\n\n// ── Expand state ────────────────────────────────────────────────────────────\n\nfunction buildInitialExpanded(): Set<string | number> {\n if (props.defaultExpanded === 'all') return new Set(getAllIds(props.nodes))\n if (props.defaultExpanded === 'none') return new Set()\n return new Set(props.defaultExpanded)\n}\n\nconst expandedIds = ref<Set<string | number>>(buildInitialExpanded())\n\nfunction toggleExpand(id: string | number) {\n const next = new Set(expandedIds.value)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n expandedIds.value = next\n}\n\n// ── Selection ───────────────────────────────────────────────────────────────\n\nconst selectedRef = computed(() => props.selected ?? null)\n\nfunction selectNode(node: TreeNode) {\n emit('update:selected', selectedRef.value === node.id ? null : node.id)\n emit('node-click', node)\n}\n\n// ── Checkable ───────────────────────────────────────────────────────────────\n\nconst checkedSet = computed(() => new Set(props.checked))\n\nfunction toggleCheck(node: TreeNode) {\n const leafIds = getLeafIds(node)\n const allLeafsChecked = leafIds.every((id) => checkedSet.value.has(id))\n const next = new Set(props.checked)\n if (allLeafsChecked) {\n // Remove leaf ids + clean up any stale branch ids\n getDescendantIds(node).forEach((id) => next.delete(id))\n } else {\n leafIds.forEach((id) => next.add(id))\n }\n emit('update:checked', [...next])\n}\n\n// ── Provide context ─────────────────────────────────────────────────────────\n\nprovide<TreeContext>('m-tree', {\n selected: selectedRef,\n checkedSet,\n expandedIds,\n checkable: computed(() => props.checkable),\n selectNode,\n toggleExpand,\n toggleCheck,\n getDescendantIds,\n getLeafIds,\n})\n\n// ── Expose expand/collapse utilities ────────────────────────────────────────\n\nfunction expandAll() { expandedIds.value = new Set(getAllIds(props.nodes)) }\nfunction collapseAll() { expandedIds.value = new Set() }\n\ndefineExpose({ expandAll, collapseAll })\n</script>\n\n<template>\n <div role=\"tree\" class=\"flex flex-col\">\n <template v-if=\"nodes.length\">\n <MTreeNode\n v-for=\"node in nodes\"\n :key=\"node.id\"\n :node=\"node\"\n :depth=\"0\"\n >\n <!-- Forward all slots down the recursive tree -->\n <template v-for=\"(_, name) in $slots\" #[name]=\"slotProps\">\n <slot :name=\"name\" v-bind=\"slotProps ?? {}\" />\n </template>\n </MTreeNode>\n </template>\n\n <div v-else class=\"flex flex-col items-center gap-2 py-10 text-on-surface-variant\">\n <MIcon name=\"account_tree\" :size=\"32\" class=\"opacity-30\" />\n <p class=\"text-body-medium\">{{ emptyText }}</p>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface TreeTableColumn {\n key: string\n label: string\n width?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport interface TreeTableRow {\n [key: string]: any\n children?: TreeTableRow[]\n}\n\nconst props = withDefaults(defineProps<{\n columns: TreeTableColumn[]\n rows: TreeTableRow[]\n rowKey?: string\n defaultExpanded?: boolean\n indent?: number\n dense?: boolean\n}>(), {\n rowKey: 'id',\n defaultExpanded: false,\n indent: 24,\n dense: false,\n})\n\nconst emit = defineEmits<{ rowClick: [TreeTableRow] }>()\n\nconst expanded = ref<Set<any>>(new Set(\n props.defaultExpanded ? collectIds(props.rows) : []\n))\n\nfunction collectIds(rows: TreeTableRow[]): any[] {\n const ids: any[] = []\n for (const r of rows) {\n if (r.children?.length) {\n ids.push(r[props.rowKey])\n ids.push(...collectIds(r.children))\n }\n }\n return ids\n}\n\nfunction toggleExpand(row: TreeTableRow) {\n const id = row[props.rowKey]\n const next = new Set(expanded.value)\n next.has(id) ? next.delete(id) : next.add(id)\n expanded.value = next\n}\n\nfunction isExpanded(row: TreeTableRow) { return expanded.value.has(row[props.rowKey]) }\n\ninterface FlatRow {\n row: TreeTableRow\n depth: number\n hasChildren: boolean\n isExpanded: boolean\n}\n\nconst flatRows = computed(() => {\n const result: FlatRow[] = []\n function walk(rows: TreeTableRow[], depth: number) {\n for (const row of rows) {\n const hasChildren = !!row.children?.length\n const exp = isExpanded(row)\n result.push({ row, depth, hasChildren, isExpanded: exp })\n if (hasChildren && exp) walk(row.children!, depth + 1)\n }\n }\n walk(props.rows, 0)\n return result\n})\n\nfunction expandAll() {\n expanded.value = new Set(collectIds(props.rows))\n}\nfunction collapseAll() {\n expanded.value = new Set()\n}\n\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n <!-- Toolbar -->\n <div v-if=\"$slots.toolbar\" class=\"flex items-center gap-2 border-b border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <slot name=\"toolbar\" :expand-all=\"expandAll\" :collapse-all=\"collapseAll\" />\n </div>\n\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead>\n <tr class=\"bg-surface-container-high\">\n <th\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n :style=\"col.width ? { width: col.width } : undefined\"\n :class=\"[\n 'whitespace-nowrap text-label-medium font-medium text-on-surface-variant',\n dense ? 'px-3 py-2' : 'px-4 py-3',\n alignClass(col.align),\n ]\"\n >\n {{ col.label }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(item, i) in flatRows\"\n :key=\"item.row[rowKey] ?? i\"\n class=\"border-t border-outline-variant transition-colors duration-100 hover:bg-on-surface/[0.04]\"\n :class=\"item.depth > 0 ? 'bg-surface-container-lowest/50' : ''\"\n @click=\"emit('rowClick', item.row)\"\n >\n <td\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n :class=\"['text-body-medium text-on-surface', alignClass(col.align), dense ? 'px-3 py-1.5' : 'px-4 py-3']\"\n >\n <!-- First column gets indent + expand icon -->\n <div v-if=\"ci === 0\" class=\"flex items-center gap-1\" :style=\"{ paddingLeft: `${item.depth * indent}px` }\">\n <button\n v-if=\"item.hasChildren\"\n type=\"button\"\n class=\"flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-transform duration-200 hover:bg-on-surface/8\"\n :class=\"item.isExpanded ? 'rotate-90' : ''\"\n @click.stop=\"toggleExpand(item.row)\"\n >\n <MIcon name=\"chevron_right\" :size=\"18\" />\n </button>\n <span v-else class=\"w-6 shrink-0\" />\n <slot :name=\"`cell-${col.key}`\" :row=\"item.row\" :value=\"item.row[col.key]\" :depth=\"item.depth\">\n <span :class=\"item.hasChildren ? 'font-medium' : ''\">{{ item.row[col.key] ?? '—' }}</span>\n </slot>\n </div>\n <template v-else>\n <slot :name=\"`cell-${col.key}`\" :row=\"item.row\" :value=\"item.row[col.key]\" :depth=\"item.depth\">\n {{ item.row[col.key] ?? '—' }}\n </slot>\n </template>\n </td>\n </tr>\n <tr v-if=\"!flatRows.length\">\n <td :colspan=\"columns.length\" class=\"border-t border-outline-variant px-4 py-10 text-center\">\n <MIcon name=\"account_tree\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">Sin datos</p>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface TreeTableColumn {\n key: string\n label: string\n width?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport interface TreeTableRow {\n [key: string]: any\n children?: TreeTableRow[]\n}\n\nconst props = withDefaults(defineProps<{\n columns: TreeTableColumn[]\n rows: TreeTableRow[]\n rowKey?: string\n defaultExpanded?: boolean\n indent?: number\n dense?: boolean\n}>(), {\n rowKey: 'id',\n defaultExpanded: false,\n indent: 24,\n dense: false,\n})\n\nconst emit = defineEmits<{ rowClick: [TreeTableRow] }>()\n\nconst expanded = ref<Set<any>>(new Set(\n props.defaultExpanded ? collectIds(props.rows) : []\n))\n\nfunction collectIds(rows: TreeTableRow[]): any[] {\n const ids: any[] = []\n for (const r of rows) {\n if (r.children?.length) {\n ids.push(r[props.rowKey])\n ids.push(...collectIds(r.children))\n }\n }\n return ids\n}\n\nfunction toggleExpand(row: TreeTableRow) {\n const id = row[props.rowKey]\n const next = new Set(expanded.value)\n next.has(id) ? next.delete(id) : next.add(id)\n expanded.value = next\n}\n\nfunction isExpanded(row: TreeTableRow) { return expanded.value.has(row[props.rowKey]) }\n\ninterface FlatRow {\n row: TreeTableRow\n depth: number\n hasChildren: boolean\n isExpanded: boolean\n}\n\nconst flatRows = computed(() => {\n const result: FlatRow[] = []\n function walk(rows: TreeTableRow[], depth: number) {\n for (const row of rows) {\n const hasChildren = !!row.children?.length\n const exp = isExpanded(row)\n result.push({ row, depth, hasChildren, isExpanded: exp })\n if (hasChildren && exp) walk(row.children!, depth + 1)\n }\n }\n walk(props.rows, 0)\n return result\n})\n\nfunction expandAll() {\n expanded.value = new Set(collectIds(props.rows))\n}\nfunction collapseAll() {\n expanded.value = new Set()\n}\n\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n <!-- Toolbar -->\n <div v-if=\"$slots.toolbar\" class=\"flex items-center gap-2 border-b border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <slot name=\"toolbar\" :expand-all=\"expandAll\" :collapse-all=\"collapseAll\" />\n </div>\n\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead>\n <tr class=\"bg-surface-container-high\">\n <th\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n :style=\"col.width ? { width: col.width } : undefined\"\n :class=\"[\n 'whitespace-nowrap text-label-medium font-medium text-on-surface-variant',\n dense ? 'px-3 py-2' : 'px-4 py-3',\n alignClass(col.align),\n ]\"\n >\n {{ col.label }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(item, i) in flatRows\"\n :key=\"item.row[rowKey] ?? i\"\n class=\"border-t border-outline-variant transition-colors duration-100 hover:bg-on-surface/[0.04]\"\n :class=\"item.depth > 0 ? 'bg-surface-container-lowest/50' : ''\"\n @click=\"emit('rowClick', item.row)\"\n >\n <td\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n :class=\"['text-body-medium text-on-surface', alignClass(col.align), dense ? 'px-3 py-1.5' : 'px-4 py-3']\"\n >\n <!-- First column gets indent + expand icon -->\n <div v-if=\"ci === 0\" class=\"flex items-center gap-1\" :style=\"{ paddingLeft: `${item.depth * indent}px` }\">\n <button\n v-if=\"item.hasChildren\"\n type=\"button\"\n class=\"flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-transform duration-200 hover:bg-on-surface/8\"\n :class=\"item.isExpanded ? 'rotate-90' : ''\"\n @click.stop=\"toggleExpand(item.row)\"\n >\n <MIcon name=\"chevron_right\" :size=\"18\" />\n </button>\n <span v-else class=\"w-6 shrink-0\" />\n <slot :name=\"`cell-${col.key}`\" :row=\"item.row\" :value=\"item.row[col.key]\" :depth=\"item.depth\">\n <span :class=\"item.hasChildren ? 'font-medium' : ''\">{{ item.row[col.key] ?? '—' }}</span>\n </slot>\n </div>\n <template v-else>\n <slot :name=\"`cell-${col.key}`\" :row=\"item.row\" :value=\"item.row[col.key]\" :depth=\"item.depth\">\n {{ item.row[col.key] ?? '—' }}\n </slot>\n </template>\n </td>\n </tr>\n <tr v-if=\"!flatRows.length\">\n <td :colspan=\"columns.length\" class=\"border-t border-outline-variant px-4 py-10 text-center\">\n <MIcon name=\"account_tree\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">Sin datos</p>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface VTableColumn {\n key: string\n label: string\n width?: string\n align?: 'left' | 'center' | 'right'\n sortable?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n columns: VTableColumn[]\n rows: Record<string, any>[]\n rowHeight?: number\n rowKey?: string\n overscan?: number\n maxHeight?: string\n}>(), {\n rowHeight: 44,\n rowKey: 'id',\n overscan: 5,\n maxHeight: '500px',\n})\n\nconst emit = defineEmits<{ rowClick: [Record<string, any>] }>()\n\nconst scrollEl = ref<HTMLElement>()\nconst scrollTop = ref(0)\nconst containerH = ref(400)\n\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) { sortKey.value = key; sortDir.value = 'asc' }\n else if (sortDir.value === 'asc') sortDir.value = 'desc'\n else { sortKey.value = ''; sortDir.value = '' }\n}\n\nconst sortedRows = computed(() => {\n if (!sortKey.value || !sortDir.value) return props.rows\n const key = sortKey.value, dir = sortDir.value\n return [...props.rows].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, { numeric: true, sensitivity: 'base' })\n return dir === 'asc' ? cmp : -cmp\n })\n})\n\nconst totalHeight = computed(() => sortedRows.value.length * props.rowHeight)\n\nconst visibleRange = computed(() => {\n const start = Math.max(0, Math.floor(scrollTop.value / props.rowHeight) - props.overscan)\n const end = Math.min(\n sortedRows.value.length,\n Math.ceil((scrollTop.value + containerH.value) / props.rowHeight) + props.overscan\n )\n return { start, end }\n})\n\nconst visibleRows = computed(() =>\n sortedRows.value.slice(visibleRange.value.start, visibleRange.value.end).map((row, i) => ({\n row,\n index: visibleRange.value.start + i,\n top: (visibleRange.value.start + i) * props.rowHeight,\n }))\n)\n\nfunction onScroll() {\n if (!scrollEl.value) return\n scrollTop.value = scrollEl.value.scrollTop\n}\n\nlet ro: ResizeObserver | null = null\nonMounted(() => {\n if (scrollEl.value) {\n containerH.value = scrollEl.value.clientHeight\n ro = new ResizeObserver((entries) => { containerH.value = entries[0]!.contentRect.height })\n ro.observe(scrollEl.value)\n }\n})\nonBeforeUnmount(() => ro?.disconnect())\n\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex bg-surface-container-high\">\n <div\n v-for=\"col in columns\"\n :key=\"col.key\"\n :style=\"{ width: col.width || 'auto', flex: col.width ? 'none' : '1' }\"\n :class=\"[\n 'px-4 py-3 text-label-medium font-medium text-on-surface-variant whitespace-nowrap',\n alignClass(col.align),\n col.sortable ? 'cursor-pointer select-none hover:text-on-surface transition-colors' : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon v-if=\"sortKey === col.key && sortDir === 'asc'\" name=\"arrow_upward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else-if=\"sortKey === col.key && sortDir === 'desc'\" name=\"arrow_downward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n </div>\n </div>\n\n <!-- Virtual scroll body -->\n <div\n ref=\"scrollEl\"\n class=\"overflow-y-auto\"\n :style=\"{ maxHeight }\"\n @scroll=\"onScroll\"\n >\n <div class=\"relative\" :style=\"{ height: `${totalHeight}px` }\">\n <div\n v-for=\"{ row, index, top } in visibleRows\"\n :key=\"row[rowKey] ?? index\"\n class=\"absolute left-0 right-0 flex border-t border-outline-variant transition-colors duration-75 hover:bg-on-surface/[0.04]\"\n :class=\"index % 2 === 0 ? '' : 'bg-surface-container-lowest/50'\"\n :style=\"{ top: `${top}px`, height: `${rowHeight}px` }\"\n @click=\"emit('rowClick', row)\"\n >\n <div\n v-for=\"col in columns\"\n :key=\"col.key\"\n class=\"flex items-center overflow-hidden px-4 text-body-medium text-on-surface\"\n :style=\"{ width: col.width || 'auto', flex: col.width ? 'none' : '1' }\"\n :class=\"alignClass(col.align)\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\">\n <span class=\"truncate\">{{ row[col.key] ?? '—' }}</span>\n </slot>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ sortedRows.length.toLocaleString() }} filas\n <template v-if=\"visibleRange.end - visibleRange.start < sortedRows.length\">\n · mostrando {{ visibleRange.start + 1 }}–{{ visibleRange.end }}\n </template>\n </span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface VTableColumn {\n key: string\n label: string\n width?: string\n align?: 'left' | 'center' | 'right'\n sortable?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n columns: VTableColumn[]\n rows: Record<string, any>[]\n rowHeight?: number\n rowKey?: string\n overscan?: number\n maxHeight?: string\n}>(), {\n rowHeight: 44,\n rowKey: 'id',\n overscan: 5,\n maxHeight: '500px',\n})\n\nconst emit = defineEmits<{ rowClick: [Record<string, any>] }>()\n\nconst scrollEl = ref<HTMLElement>()\nconst scrollTop = ref(0)\nconst containerH = ref(400)\n\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) { sortKey.value = key; sortDir.value = 'asc' }\n else if (sortDir.value === 'asc') sortDir.value = 'desc'\n else { sortKey.value = ''; sortDir.value = '' }\n}\n\nconst sortedRows = computed(() => {\n if (!sortKey.value || !sortDir.value) return props.rows\n const key = sortKey.value, dir = sortDir.value\n return [...props.rows].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, { numeric: true, sensitivity: 'base' })\n return dir === 'asc' ? cmp : -cmp\n })\n})\n\nconst totalHeight = computed(() => sortedRows.value.length * props.rowHeight)\n\nconst visibleRange = computed(() => {\n const start = Math.max(0, Math.floor(scrollTop.value / props.rowHeight) - props.overscan)\n const end = Math.min(\n sortedRows.value.length,\n Math.ceil((scrollTop.value + containerH.value) / props.rowHeight) + props.overscan\n )\n return { start, end }\n})\n\nconst visibleRows = computed(() =>\n sortedRows.value.slice(visibleRange.value.start, visibleRange.value.end).map((row, i) => ({\n row,\n index: visibleRange.value.start + i,\n top: (visibleRange.value.start + i) * props.rowHeight,\n }))\n)\n\nfunction onScroll() {\n if (!scrollEl.value) return\n scrollTop.value = scrollEl.value.scrollTop\n}\n\nlet ro: ResizeObserver | null = null\nonMounted(() => {\n if (scrollEl.value) {\n containerH.value = scrollEl.value.clientHeight\n ro = new ResizeObserver((entries) => { containerH.value = entries[0]!.contentRect.height })\n ro.observe(scrollEl.value)\n }\n})\nonBeforeUnmount(() => ro?.disconnect())\n\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex bg-surface-container-high\">\n <div\n v-for=\"col in columns\"\n :key=\"col.key\"\n :style=\"{ width: col.width || 'auto', flex: col.width ? 'none' : '1' }\"\n :class=\"[\n 'px-4 py-3 text-label-medium font-medium text-on-surface-variant whitespace-nowrap',\n alignClass(col.align),\n col.sortable ? 'cursor-pointer select-none hover:text-on-surface transition-colors' : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon v-if=\"sortKey === col.key && sortDir === 'asc'\" name=\"arrow_upward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else-if=\"sortKey === col.key && sortDir === 'desc'\" name=\"arrow_downward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n </div>\n </div>\n\n <!-- Virtual scroll body -->\n <div\n ref=\"scrollEl\"\n class=\"overflow-y-auto\"\n :style=\"{ maxHeight }\"\n @scroll=\"onScroll\"\n >\n <div class=\"relative\" :style=\"{ height: `${totalHeight}px` }\">\n <div\n v-for=\"{ row, index, top } in visibleRows\"\n :key=\"row[rowKey] ?? index\"\n class=\"absolute left-0 right-0 flex border-t border-outline-variant transition-colors duration-75 hover:bg-on-surface/[0.04]\"\n :class=\"index % 2 === 0 ? '' : 'bg-surface-container-lowest/50'\"\n :style=\"{ top: `${top}px`, height: `${rowHeight}px` }\"\n @click=\"emit('rowClick', row)\"\n >\n <div\n v-for=\"col in columns\"\n :key=\"col.key\"\n class=\"flex items-center overflow-hidden px-4 text-body-medium text-on-surface\"\n :style=\"{ width: col.width || 'auto', flex: col.width ? 'none' : '1' }\"\n :class=\"alignClass(col.align)\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\">\n <span class=\"truncate\">{{ row[col.key] ?? '—' }}</span>\n </slot>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ sortedRows.length.toLocaleString() }} filas\n <template v-if=\"visibleRange.end - visibleRange.start < sortedRows.length\">\n · mostrando {{ visibleRange.start + 1 }}–{{ visibleRange.end }}\n </template>\n </span>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"m3ui.js","names":["$slots","$slots","$slots","$emit","$slots","$slots","$slots","$slots","$slots","$emit","$slots","$slots","$emit","$slots","$slots","$slots","$slots","$slots","$slots","$slots","$slots","$emit","$slots","$slots","$slots","$slots"],"sources":["../src/plugin.ts","../src/composables/useTheme.ts","../src/composables/useColorPalette.ts","../src/composables/useToast.ts","../src/composables/useFieldBg.ts","../src/components/MAlert.vue","../src/components/MAlert.vue","../src/components/MAppBar.vue","../src/components/MAppBar.vue","../src/components/MAvatar.vue","../src/components/MAvatar.vue","../src/components/MBadge.vue","../src/components/MBadge.vue","../src/components/MBottomSheet.vue","../src/components/MBottomSheet.vue","../src/components/MBreadcrumbs.vue","../src/components/MBreadcrumbs.vue","../src/components/MSpinner.vue","../src/components/MSpinner.vue","../src/components/MButton.vue","../src/components/MButton.vue","../src/components/MIconButton.vue","../src/components/MIconButton.vue","../src/components/MCalendar.vue","../src/components/MCalendar.vue","../src/components/MCard.vue","../src/components/MCard.vue","../src/components/MCheckbox.vue","../src/components/MCheckbox.vue","../src/components/MChip.vue","../src/components/MChip.vue","../src/components/MColorPicker.vue","../src/components/MColorPicker.vue","../src/components/MCommandPalette.vue","../src/components/MCommandPalette.vue","../src/components/MDialog.vue","../src/components/MDialog.vue","../src/components/MConfirmDialog.vue","../src/components/MConfirmDialog.vue","../src/components/MContainer.vue","../src/components/MContainer.vue","../src/components/_MContextMenuPanel.vue","../src/components/_MContextMenuPanel.vue","../src/components/MContextMenu.vue","../src/components/MContextMenu.vue","../src/components/MPagination.vue","../src/components/MPagination.vue","../src/components/MDataTable.vue","../src/components/MDataTable.vue","../src/components/MDatePicker.vue","../src/components/MDatePicker.vue","../src/components/MDateRangePicker.vue","../src/components/MDateRangePicker.vue","../src/components/MDivider.vue","../src/components/MDivider.vue","../src/components/MDragDropList.vue","../src/components/MDragDropList.vue","../src/components/MEmptyState.vue","../src/components/MEmptyState.vue","../src/components/MExpansionPanel.vue","../src/components/MExpansionPanel.vue","../src/components/MFab.vue","../src/components/MFab.vue","../src/components/MFileUpload.vue","../src/components/MFileUpload.vue","../src/components/MGrid.vue","../src/components/MGrid.vue","../src/components/MHotkeys.vue","../src/components/MHotkeys.vue","../src/components/MInfiniteScroll.vue","../src/components/MInfiniteScroll.vue","../src/components/MJsonViewer.vue","../src/components/MJsonViewer.vue","../src/components/MKanban.vue","../src/components/MKanban.vue","../src/components/MLoadingOverlay.vue","../src/components/MLoadingOverlay.vue","../src/components/MMasonry.vue","../src/components/MMasonry.vue","../src/components/MMenu.vue","../src/components/MMenu.vue","../src/components/MMenuItem.vue","../src/components/MMenuItem.vue","../src/components/MMultiSelect.vue","../src/components/MMultiSelect.vue","../src/components/MNavigationBar.vue","../src/components/MNavigationBar.vue","../src/components/MNavigationDrawer.vue","../src/components/MNavigationDrawer.vue","../src/components/MNavigationRail.vue","../src/components/MNavigationRail.vue","../src/components/MProgressBar.vue","../src/components/MProgressBar.vue","../src/components/MRadio.vue","../src/components/MRadio.vue","../src/components/MRadioGroup.vue","../src/components/MRadioGroup.vue","../src/components/MRating.vue","../src/components/MRating.vue","../src/components/MResult.vue","../src/components/MResult.vue","../src/components/MScheduler.vue","../src/components/MScheduler.vue","../src/components/MSegmentedButton.vue","../src/components/MSegmentedButton.vue","../src/components/MSelect.vue","../src/components/MSelect.vue","../src/components/MSideSheet.vue","../src/components/MSideSheet.vue","../src/components/MSkeleton.vue","../src/components/MSkeleton.vue","../src/components/MSlider.vue","../src/components/MSlider.vue","../src/components/MSnackbar.vue","../src/components/MSnackbar.vue","../src/components/MSplitter.vue","../src/components/MSplitter.vue","../src/components/MSpotlightSearch.vue","../src/components/MSpotlightSearch.vue","../src/components/MStack.vue","../src/components/MStack.vue","../src/components/MStatCard.vue","../src/components/MStatCard.vue","../src/components/MStepper.vue","../src/components/MStepper.vue","../src/components/MSwitch.vue","../src/components/MSwitch.vue","../src/components/MTable.vue","../src/components/MTable.vue","../src/components/MTabs.vue","../src/components/MTabs.vue","../src/components/MTextField.vue","../src/components/MTextField.vue","../src/components/MTimeline.vue","../src/components/MTimeline.vue","../src/components/MTimePicker.vue","../src/components/MTimePicker.vue","../src/components/MTooltip.vue","../src/components/MTooltip.vue","../src/components/MTopAppBar.vue","../src/components/MTopAppBar.vue","../src/components/MTour.vue","../src/components/MTour.vue","../src/components/MTransferList.vue","../src/components/MTransferList.vue","../src/components/_MTreeNode.vue","../src/components/_MTreeNode.vue","../src/components/MTree.vue","../src/components/MTree.vue","../src/components/MTreeTable.vue","../src/components/MTreeTable.vue","../src/components/MVirtualTable.vue","../src/components/MVirtualTable.vue"],"sourcesContent":["import type { App } from 'vue'\nimport type { Palette } from './composables/useColorPalette'\n\nexport interface M3UIOptions {\n palette?: string\n customPalettes?: Palette[]\n}\n\nexport function createM3UI(options: M3UIOptions = {}) {\n return {\n install(_app: App) {\n if (options.palette && options.palette !== 'purple') {\n document.documentElement.setAttribute('data-palette', options.palette)\n localStorage.setItem('m3-palette', options.palette)\n }\n },\n }\n}\n","import { ref, watchEffect, onMounted, onUnmounted } from 'vue'\n\nexport type Theme = 'light' | 'dark' | 'system'\n\nconst theme = ref<Theme>((localStorage.getItem('m3-theme') as Theme) ?? 'system')\n\nlet _appliedDark: boolean | null = null\n\nfunction applyTheme(t: Theme) {\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches\n const isDark = t === 'dark' || (t === 'system' && prefersDark)\n\n const isChange = _appliedDark !== null && _appliedDark !== isDark\n\n if (isChange) {\n // Force reflow so transition property takes effect before the color change\n document.documentElement.classList.add('theme-transitioning')\n void document.documentElement.offsetHeight\n }\n\n document.documentElement.classList.toggle('dark', isDark)\n\n if (isChange) {\n setTimeout(() => document.documentElement.classList.remove('theme-transitioning'), 300)\n }\n\n _appliedDark = isDark\n}\n\n// Apply immediately on module load (before any component mounts) — no animation\napplyTheme(theme.value)\n\nexport function useTheme() {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n\n function onSystemChange() {\n if (theme.value === 'system') applyTheme('system')\n }\n\n watchEffect(() => {\n localStorage.setItem('m3-theme', theme.value)\n applyTheme(theme.value)\n })\n\n onMounted(() => mediaQuery.addEventListener('change', onSystemChange))\n onUnmounted(() => mediaQuery.removeEventListener('change', onSystemChange))\n\n function cycle() {\n const order: Theme[] = ['light', 'dark', 'system']\n const idx = order.indexOf(theme.value)\n theme.value = order[(idx + 1) % order.length]!\n }\n\n return { theme, cycle }\n}\n","import { ref, watchEffect } from 'vue'\n\nexport interface Palette {\n id: string\n label: string\n seed: string\n}\n\nexport const palettes: Palette[] = [\n { id: 'purple', label: 'Morado', seed: '#6750A4' },\n { id: 'indigo', label: 'Índigo', seed: '#4355B9' },\n { id: 'navy', label: 'Marino', seed: '#354BA0' },\n { id: 'blue', label: 'Azul', seed: '#005AC1' },\n { id: 'cyan', label: 'Cian', seed: '#006874' },\n { id: 'teal', label: 'Teal', seed: '#006B5F' },\n { id: 'green', label: 'Verde', seed: '#386A20' },\n { id: 'lime', label: 'Lima', seed: '#4C6706' },\n { id: 'olive', label: 'Oliva', seed: '#636118' },\n { id: 'amber', label: 'Ámbar', seed: '#785900' },\n { id: 'sand', label: 'Arena', seed: '#715C2E' },\n { id: 'orange', label: 'Naranja', seed: '#8B5000' },\n { id: 'deep-orange', label: 'Naranja oscuro', seed: '#96480A' },\n { id: 'brown', label: 'Marrón', seed: '#6E4C32' },\n { id: 'red', label: 'Rojo', seed: '#B82000' },\n { id: 'coral', label: 'Coral', seed: '#A03530' },\n { id: 'crimson', label: 'Carmesí', seed: '#9C4068' },\n { id: 'pink', label: 'Rosa', seed: '#9C4057' },\n { id: 'violet', label: 'Violeta', seed: '#7C39A4' },\n { id: 'slate', label: 'Pizarra', seed: '#4A6269' },\n]\n\nconst current = ref(localStorage.getItem('m3-palette') ?? 'purple')\n\nexport function useColorPalette() {\n watchEffect(() => {\n const id = current.value\n localStorage.setItem('m3-palette', id)\n\n if (id === 'purple') {\n document.documentElement.removeAttribute('data-palette')\n } else {\n document.documentElement.setAttribute('data-palette', id)\n }\n })\n\n function set(id: string) {\n document.documentElement.classList.add('theme-transitioning')\n void document.documentElement.offsetHeight\n current.value = id\n setTimeout(() => document.documentElement.classList.remove('theme-transitioning'), 300)\n }\n\n return { palette: current, palettes, set }\n}\n\n// Apply on module load so palette is visible before any component mounts\nconst saved = localStorage.getItem('m3-palette')\nif (saved && saved !== 'purple') {\n document.documentElement.setAttribute('data-palette', saved)\n}\n","import { ref } from 'vue'\n\nexport type ToastVariant = 'info' | 'success' | 'warning' | 'error'\nexport type ToastPosition =\n | 'top-left' | 'top-center' | 'top-right'\n | 'bottom-left' | 'bottom-center' | 'bottom-right'\n\nexport interface ToastAction { label: string; onClick: () => void }\n\nexport interface Toast {\n id: number\n message: string\n variant: ToastVariant\n duration: number\n action?: ToastAction\n}\n\nlet nextId = 1\n\nconst toasts = ref<Toast[]>([])\nconst position = ref<ToastPosition>('bottom-center')\n\nfunction dismiss(id: number) {\n toasts.value = toasts.value.filter((t) => t.id !== id)\n}\n\nfunction show(\n message: string,\n variant: ToastVariant = 'info',\n options: number | { duration?: number; action?: ToastAction } = {},\n) {\n const id = nextId++\n const opts = typeof options === 'number' ? { duration: options } : options\n const duration = opts.duration ?? (variant === 'error' ? 6000 : 4000)\n toasts.value.push({ id, message, variant, duration, action: opts.action })\n if (duration > 0) setTimeout(() => dismiss(id), duration)\n return id\n}\n\nconst success = (msg: string, opts?: { duration?: number; action?: ToastAction }) =>\n show(msg, 'success', opts ?? {})\nconst error = (msg: string, opts?: { duration?: number; action?: ToastAction }) =>\n show(msg, 'error', opts ?? {})\nconst warning = (msg: string, opts?: { duration?: number; action?: ToastAction }) =>\n show(msg, 'warning', opts ?? {})\nconst info = (msg: string, opts?: { duration?: number; action?: ToastAction }) =>\n show(msg, 'info', opts ?? {})\n\nexport function useToast() {\n return { toasts, position, show, success, error, warning, info, dismiss }\n}\n","import { ref, computed, onMounted, onBeforeUnmount, type Ref } from 'vue'\n\nconst M3_BG_CLASSES = [\n 'bg-surface-container-highest',\n 'bg-surface-container-high',\n 'bg-surface-container-low',\n 'bg-surface-container-lowest',\n 'bg-surface-container',\n 'bg-surface-variant',\n 'bg-surface-bright',\n 'bg-surface-dim',\n 'bg-surface',\n 'bg-background',\n 'bg-inverse-surface',\n 'bg-primary-container',\n 'bg-secondary-container',\n 'bg-tertiary-container',\n 'bg-error-container',\n 'bg-primary',\n 'bg-secondary',\n 'bg-tertiary',\n 'bg-error',\n] as const\n\nfunction findM3BgVar(el: HTMLElement): string | null {\n for (const cls of M3_BG_CLASSES) {\n if (el.classList.contains(cls)) return `var(--color-${cls.slice(3)})`\n }\n return null\n}\n\nfunction isTransparent(color: string): boolean {\n if (!color || color === 'transparent') return true\n const m = color.match(/^rgba?\\(([^)]+)\\)$/)\n if (m) {\n const parts = m[1]!.split(',').map((s) => s.trim())\n if (parts.length === 4 && parseFloat(parts[3]!) === 0) return true\n }\n return false\n}\n\n/**\n * Auto-detects the background behind `containerEl` and exposes it as `--field-bg`.\n * Prefers a CSS variable reference (e.g. var(--color-surface-container-low)) over a\n * raw computed color so the outlined label cutout transitions in sync with the rest\n * of the theme switch instead of lagging behind.\n *\n * @param containerEl The element that receives `--field-bg` as an inline style.\n * @param fieldBgProp Getter for the explicit `fieldBg` prop (overrides auto-detect).\n */\nexport function useFieldBg(\n containerEl: Ref<HTMLElement | null>,\n fieldBgProp: () => string | undefined,\n) {\n const detectedBg = ref<string>('var(--color-surface)')\n\n function applyFieldBg(value: string) {\n detectedBg.value = value\n containerEl.value?.style.setProperty('--field-bg', fieldBgProp() ?? value)\n }\n\n function resolveBg() {\n let el: HTMLElement | null = containerEl.value?.parentElement ?? null\n while (el) {\n const cssVar = findM3BgVar(el)\n if (cssVar) { applyFieldBg(cssVar); return }\n if (el === document.body) { applyFieldBg('var(--color-surface)'); return }\n const bg = getComputedStyle(el).backgroundColor\n if (!isTransparent(bg)) { applyFieldBg(bg); return }\n el = el.parentElement\n }\n applyFieldBg('var(--color-surface)')\n }\n\n let observer: MutationObserver | null = null\n\n onMounted(() => {\n resolveBg()\n observer = new MutationObserver(() => resolveBg())\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'style', 'data-theme'],\n })\n })\n\n onBeforeUnmount(() => observer?.disconnect())\n\n const resolvedFieldBg = computed(() => fieldBgProp() ?? detectedBg.value)\n\n return { resolvedFieldBg }\n}\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n type?: 'info' | 'success' | 'warning' | 'error'\n title?: string\n closeable?: boolean\n }>(),\n {\n type: 'info',\n closeable: false,\n },\n)\n\nconst emit = defineEmits<{ close: [] }>()\n\nconst config = {\n info: {\n icon: 'info',\n container: 'bg-primary-container text-on-primary-container',\n iconColor: 'text-primary',\n },\n success: {\n icon: 'check_circle',\n container: 'bg-success-container text-on-success-container',\n iconColor: 'text-success',\n },\n warning: {\n icon: 'warning',\n container: 'bg-tertiary-container text-on-tertiary-container',\n iconColor: 'text-tertiary',\n },\n error: {\n icon: 'error',\n container: 'bg-error-container text-on-error-container',\n iconColor: 'text-error',\n },\n}\n</script>\n\n<template>\n <div class=\"flex items-start gap-3 rounded-md p-4\" :class=\"config[type].container\">\n <MIcon\n :name=\"config[type].icon\"\n :size=\"20\"\n class=\"mt-0.5 shrink-0\"\n :class=\"config[type].iconColor\"\n />\n <div class=\"min-w-0 flex-1\">\n <p v-if=\"title\" class=\"mb-0.5 text-label-large font-medium\">{{ title }}</p>\n <div class=\"text-body-medium\">\n <slot />\n </div>\n <div v-if=\"$slots.actions\" class=\"mt-3 flex flex-wrap gap-2\">\n <slot name=\"actions\" />\n </div>\n </div>\n <button\n v-if=\"closeable\"\n type=\"button\"\n class=\"-mr-1 flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-on-surface/8\"\n aria-label=\"Cerrar\"\n @click=\"emit('close')\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n type?: 'info' | 'success' | 'warning' | 'error'\n title?: string\n closeable?: boolean\n }>(),\n {\n type: 'info',\n closeable: false,\n },\n)\n\nconst emit = defineEmits<{ close: [] }>()\n\nconst config = {\n info: {\n icon: 'info',\n container: 'bg-primary-container text-on-primary-container',\n iconColor: 'text-primary',\n },\n success: {\n icon: 'check_circle',\n container: 'bg-success-container text-on-success-container',\n iconColor: 'text-success',\n },\n warning: {\n icon: 'warning',\n container: 'bg-tertiary-container text-on-tertiary-container',\n iconColor: 'text-tertiary',\n },\n error: {\n icon: 'error',\n container: 'bg-error-container text-on-error-container',\n iconColor: 'text-error',\n },\n}\n</script>\n\n<template>\n <div class=\"flex items-start gap-3 rounded-md p-4\" :class=\"config[type].container\">\n <MIcon\n :name=\"config[type].icon\"\n :size=\"20\"\n class=\"mt-0.5 shrink-0\"\n :class=\"config[type].iconColor\"\n />\n <div class=\"min-w-0 flex-1\">\n <p v-if=\"title\" class=\"mb-0.5 text-label-large font-medium\">{{ title }}</p>\n <div class=\"text-body-medium\">\n <slot />\n </div>\n <div v-if=\"$slots.actions\" class=\"mt-3 flex flex-wrap gap-2\">\n <slot name=\"actions\" />\n </div>\n </div>\n <button\n v-if=\"closeable\"\n type=\"button\"\n class=\"-mr-1 flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-on-surface/8\"\n aria-label=\"Cerrar\"\n @click=\"emit('close')\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n color?: 'surface' | 'primary' | 'secondary' | 'tertiary'\n elevated?: boolean\n dense?: boolean\n}>(), { color: 'surface' })\n\nconst colorMap: Record<string, string> = {\n surface: 'bg-surface text-on-surface',\n primary: 'bg-primary text-on-primary',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n}\n</script>\n\n<template>\n <div\n class=\"flex w-full items-center gap-2 px-4 transition-shadow\"\n :class=\"[\n colorMap[color],\n elevated ? 'shadow-elevation-2' : '',\n dense ? 'h-12' : 'h-16',\n ]\"\n >\n <!-- Leading -->\n <div v-if=\"$slots.leading\" class=\"flex shrink-0 items-center\">\n <slot name=\"leading\" />\n </div>\n\n <!-- Content -->\n <div class=\"flex flex-1 items-center overflow-hidden\">\n <slot />\n </div>\n\n <!-- Trailing -->\n <div v-if=\"$slots.trailing\" class=\"flex shrink-0 items-center gap-1\">\n <slot name=\"trailing\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n color?: 'surface' | 'primary' | 'secondary' | 'tertiary'\n elevated?: boolean\n dense?: boolean\n}>(), { color: 'surface' })\n\nconst colorMap: Record<string, string> = {\n surface: 'bg-surface text-on-surface',\n primary: 'bg-primary text-on-primary',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n}\n</script>\n\n<template>\n <div\n class=\"flex w-full items-center gap-2 px-4 transition-shadow\"\n :class=\"[\n colorMap[color],\n elevated ? 'shadow-elevation-2' : '',\n dense ? 'h-12' : 'h-16',\n ]\"\n >\n <!-- Leading -->\n <div v-if=\"$slots.leading\" class=\"flex shrink-0 items-center\">\n <slot name=\"leading\" />\n </div>\n\n <!-- Content -->\n <div class=\"flex flex-1 items-center overflow-hidden\">\n <slot />\n </div>\n\n <!-- Trailing -->\n <div v-if=\"$slots.trailing\" class=\"flex shrink-0 items-center gap-1\">\n <slot name=\"trailing\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(defineProps<{ name: string; size?: number }>(), { size: 40 })\n\nconst initials = computed(() => {\n const parts = props.name.trim().split(/\\s+/).filter(Boolean)\n const first = parts[0]?.[0] ?? ''\n const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : ''\n return (first + last).toUpperCase() || '?'\n})\n</script>\n\n<template>\n <div\n class=\"inline-flex shrink-0 items-center justify-center rounded-full bg-primary-container font-medium text-on-primary-container\"\n :style=\"{ width: `${size}px`, height: `${size}px`, fontSize: `${Math.round(size * 0.4)}px` }\"\n >\n {{ initials }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(defineProps<{ name: string; size?: number }>(), { size: 40 })\n\nconst initials = computed(() => {\n const parts = props.name.trim().split(/\\s+/).filter(Boolean)\n const first = parts[0]?.[0] ?? ''\n const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : ''\n return (first + last).toUpperCase() || '?'\n})\n</script>\n\n<template>\n <div\n class=\"inline-flex shrink-0 items-center justify-center rounded-full bg-primary-container font-medium text-on-primary-container\"\n :style=\"{ width: `${size}px`, height: `${size}px`, fontSize: `${Math.round(size * 0.4)}px` }\"\n >\n {{ initials }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n count?: number\n dot?: boolean\n color?: 'primary' | 'error' | 'secondary' | 'tertiary'\n max?: number\n }>(),\n {\n color: 'error',\n max: 99,\n },\n)\n\nconst show = computed(() => props.dot || (props.count !== undefined && props.count > 0))\n\nconst label = computed(() => {\n if (props.dot || props.count === undefined) return ''\n return props.count > props.max ? `${props.max}+` : String(props.count)\n})\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary text-on-primary',\n error: 'bg-error text-on-error',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n}\n</script>\n\n<template>\n <span class=\"relative inline-flex\">\n <slot />\n <span\n v-if=\"show\"\n class=\"absolute -right-1 -top-1 flex items-center justify-center rounded-full font-medium leading-none\"\n :class=\"[\n colorMap[color],\n !label || dot ? 'h-2.5 w-2.5' : label.length > 2 ? 'h-5 min-w-[1.25rem] px-1 text-[10px]' : 'h-5 w-5 text-[10px]',\n ]\"\n >\n <span v-if=\"!dot\">{{ label }}</span>\n </span>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n count?: number\n dot?: boolean\n color?: 'primary' | 'error' | 'secondary' | 'tertiary'\n max?: number\n }>(),\n {\n color: 'error',\n max: 99,\n },\n)\n\nconst show = computed(() => props.dot || (props.count !== undefined && props.count > 0))\n\nconst label = computed(() => {\n if (props.dot || props.count === undefined) return ''\n return props.count > props.max ? `${props.max}+` : String(props.count)\n})\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary text-on-primary',\n error: 'bg-error text-on-error',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n}\n</script>\n\n<template>\n <span class=\"relative inline-flex\">\n <slot />\n <span\n v-if=\"show\"\n class=\"absolute -right-1 -top-1 flex items-center justify-center rounded-full font-medium leading-none\"\n :class=\"[\n colorMap[color],\n !label || dot ? 'h-2.5 w-2.5' : label.length > 2 ? 'h-5 min-w-[1.25rem] px-1 text-[10px]' : 'h-5 w-5 text-[10px]',\n ]\"\n >\n <span v-if=\"!dot\">{{ label }}</span>\n </span>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n modelValue: boolean\n title?: string\n fullHeight?: boolean\n}>(), { fullHeight: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\nconst close = () => emit('update:modelValue', false)\n\n// Drag-to-dismiss state\nconst dragY = ref(0)\nconst dragging = ref(false)\nlet startY = 0\n\nfunction onHandlePointerDown(e: PointerEvent) {\n dragging.value = true\n startY = e.clientY\n dragY.value = 0\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onHandlePointerMove(e: PointerEvent) {\n if (!dragging.value) return\n dragY.value = Math.max(0, e.clientY - startY)\n}\nfunction onHandlePointerUp() {\n if (dragY.value > 100) close()\n dragging.value = false\n dragY.value = 0\n}\n\nconst sheetStyle = computed(() => ({\n transform: `translateY(${dragY.value}px)`,\n transition: dragging.value ? 'none' : undefined,\n}))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"bs\" :duration=\"{ enter: 320, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[200] flex flex-col justify-end\">\n <!-- Scrim -->\n <div class=\"bs-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <div\n class=\"bs-panel relative flex w-full flex-col rounded-t-[28px] bg-surface-container-low shadow-elevation-3\"\n :class=\"fullHeight ? 'max-h-[92vh]' : 'max-h-[60vh]'\"\n :style=\"sheetStyle\"\n >\n <!-- Drag handle -->\n <div\n class=\"flex h-9 shrink-0 cursor-grab touch-none select-none items-center justify-center active:cursor-grabbing\"\n @pointerdown=\"onHandlePointerDown\"\n @pointermove=\"onHandlePointerMove\"\n @pointerup=\"onHandlePointerUp\"\n >\n <div class=\"h-1 w-8 rounded-full bg-on-surface-variant/40\" />\n </div>\n\n <!-- Header -->\n <div v-if=\"title\" class=\"flex shrink-0 items-center justify-between px-6 pb-4\">\n <h2 class=\"text-title-large text-on-surface\">{{ title }}</h2>\n <button\n type=\"button\"\n class=\"flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"20\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"flex-1 overflow-y-auto px-6 pb-6\">\n <slot />\n </div>\n\n <!-- Actions -->\n <div v-if=\"$slots.actions\" class=\"shrink-0 border-t border-outline-variant px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.bs-scrim {\n transition: opacity 300ms ease;\n}\n.bs-enter-from .bs-scrim,\n.bs-leave-to .bs-scrim {\n opacity: 0;\n}\n\n.bs-panel {\n transition:\n transform 320ms cubic-bezier(0.2, 0, 0, 1),\n opacity 240ms ease;\n}\n.bs-enter-from .bs-panel {\n transform: translateY(40%);\n opacity: 0;\n}\n.bs-leave-to .bs-panel {\n transform: translateY(100%) !important;\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n modelValue: boolean\n title?: string\n fullHeight?: boolean\n}>(), { fullHeight: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\nconst close = () => emit('update:modelValue', false)\n\n// Drag-to-dismiss state\nconst dragY = ref(0)\nconst dragging = ref(false)\nlet startY = 0\n\nfunction onHandlePointerDown(e: PointerEvent) {\n dragging.value = true\n startY = e.clientY\n dragY.value = 0\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onHandlePointerMove(e: PointerEvent) {\n if (!dragging.value) return\n dragY.value = Math.max(0, e.clientY - startY)\n}\nfunction onHandlePointerUp() {\n if (dragY.value > 100) close()\n dragging.value = false\n dragY.value = 0\n}\n\nconst sheetStyle = computed(() => ({\n transform: `translateY(${dragY.value}px)`,\n transition: dragging.value ? 'none' : undefined,\n}))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"bs\" :duration=\"{ enter: 320, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[200] flex flex-col justify-end\">\n <!-- Scrim -->\n <div class=\"bs-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <div\n class=\"bs-panel relative flex w-full flex-col rounded-t-[28px] bg-surface-container-low shadow-elevation-3\"\n :class=\"fullHeight ? 'max-h-[92vh]' : 'max-h-[60vh]'\"\n :style=\"sheetStyle\"\n >\n <!-- Drag handle -->\n <div\n class=\"flex h-9 shrink-0 cursor-grab touch-none select-none items-center justify-center active:cursor-grabbing\"\n @pointerdown=\"onHandlePointerDown\"\n @pointermove=\"onHandlePointerMove\"\n @pointerup=\"onHandlePointerUp\"\n >\n <div class=\"h-1 w-8 rounded-full bg-on-surface-variant/40\" />\n </div>\n\n <!-- Header -->\n <div v-if=\"title\" class=\"flex shrink-0 items-center justify-between px-6 pb-4\">\n <h2 class=\"text-title-large text-on-surface\">{{ title }}</h2>\n <button\n type=\"button\"\n class=\"flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"20\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"flex-1 overflow-y-auto px-6 pb-6\">\n <slot />\n </div>\n\n <!-- Actions -->\n <div v-if=\"$slots.actions\" class=\"shrink-0 border-t border-outline-variant px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.bs-scrim {\n transition: opacity 300ms ease;\n}\n.bs-enter-from .bs-scrim,\n.bs-leave-to .bs-scrim {\n opacity: 0;\n}\n\n.bs-panel {\n transition:\n transform 320ms cubic-bezier(0.2, 0, 0, 1),\n opacity 240ms ease;\n}\n.bs-enter-from .bs-panel {\n transform: translateY(40%);\n opacity: 0;\n}\n.bs-leave-to .bs-panel {\n transform: translateY(100%) !important;\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nexport interface BreadcrumbItem {\n label: string\n icon?: string\n to?: string\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n items: BreadcrumbItem[]\n separator?: string\n}>(), { separator: 'chevron_right' })\n\ndefineEmits<{ select: [BreadcrumbItem, number] }>()\n</script>\n\n<template>\n <nav aria-label=\"Breadcrumb\" class=\"flex items-center gap-1 overflow-x-auto text-label-large\">\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <!-- Separator -->\n <MIcon\n v-if=\"i > 0\"\n :name=\"separator\"\n :size=\"18\"\n class=\"shrink-0 text-on-surface-variant\"\n />\n\n <!-- Item -->\n <button\n v-if=\"i < items.length - 1 && !item.disabled\"\n type=\"button\"\n class=\"flex shrink-0 cursor-pointer items-center gap-1.5 rounded-sm px-1.5 py-1 text-primary transition-colors hover:bg-primary/8 focus-visible:outline-none\"\n @click=\"$emit('select', item, i)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n <span>{{ item.label }}</span>\n </button>\n\n <!-- Last item (current page) or disabled -->\n <span\n v-else\n class=\"flex shrink-0 items-center gap-1.5 px-1.5 py-1\"\n :class=\"item.disabled ? 'text-on-surface/38' : 'font-medium text-on-surface'\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n <span>{{ item.label }}</span>\n </span>\n </template>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nexport interface BreadcrumbItem {\n label: string\n icon?: string\n to?: string\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n items: BreadcrumbItem[]\n separator?: string\n}>(), { separator: 'chevron_right' })\n\ndefineEmits<{ select: [BreadcrumbItem, number] }>()\n</script>\n\n<template>\n <nav aria-label=\"Breadcrumb\" class=\"flex items-center gap-1 overflow-x-auto text-label-large\">\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <!-- Separator -->\n <MIcon\n v-if=\"i > 0\"\n :name=\"separator\"\n :size=\"18\"\n class=\"shrink-0 text-on-surface-variant\"\n />\n\n <!-- Item -->\n <button\n v-if=\"i < items.length - 1 && !item.disabled\"\n type=\"button\"\n class=\"flex shrink-0 cursor-pointer items-center gap-1.5 rounded-sm px-1.5 py-1 text-primary transition-colors hover:bg-primary/8 focus-visible:outline-none\"\n @click=\"$emit('select', item, i)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n <span>{{ item.label }}</span>\n </button>\n\n <!-- Last item (current page) or disabled -->\n <span\n v-else\n class=\"flex shrink-0 items-center gap-1.5 px-1.5 py-1\"\n :class=\"item.disabled ? 'text-on-surface/38' : 'font-medium text-on-surface'\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n <span>{{ item.label }}</span>\n </span>\n </template>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n size?: number;\n wavy?: boolean;\n }>(),\n { size: 20, wavy: false },\n);\n\nconst STROKE = 3;\nconst BUMPS = 9;\n\n// amp fraction of r = 0.25 → max radius = r * 1.25\n// Constrain so that max_r + STROKE/2 ≤ size/2 - 1 (1px margin from edge)\nconst r = computed(() => (props.size / 2 - 1 - STROKE / 2) / 1.25);\nconst cx = computed(() => props.size / 2);\n\n// Build the full bumpy-circle path and its total length.\nconst wavyData = computed(() => {\n const CX = cx.value;\n const R = r.value;\n const amp = R * 0.08;\n const segs = BUMPS * 24; // smooth curve\n\n const pts: string[] = [];\n let len = 0;\n let px = 0,\n py = 0;\n\n for (let i = 0; i <= segs; i++) {\n const theta = (2 * Math.PI * i) / segs - Math.PI / 2;\n const rr = R + amp * Math.sin(BUMPS * theta);\n const x = CX + rr * Math.cos(theta);\n const y = CX + rr * Math.sin(theta);\n if (i > 0) len += Math.sqrt((x - px) ** 2 + (y - py) ** 2);\n pts.push(`${i === 0 ? \"M\" : \"L\"}${x.toFixed(2)},${y.toFixed(2)}`);\n px = x;\n py = y;\n }\n\n // Visible arc ~58% of the circumference, gap fills the rest.\n const visible = len * 0.58;\n const gap = len - visible;\n const dash = `${visible.toFixed(1)} ${gap.toFixed(1)}`;\n\n // The wave \"travels\" by shifting dashoffset over exactly one full length,\n // so the crests slide around the path independently of the rotation.\n return { path: pts.join(\"\") + \"Z\", dash, len: len.toFixed(1) };\n});\n</script>\n\n<template>\n <span\n class=\"inline-flex shrink-0 items-center justify-center\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n role=\"status\"\n aria-label=\"Cargando\"\n >\n <!-- Standard circular spinner -->\n <span\n v-if=\"!wavy\"\n class=\"block h-full w-full animate-spin rounded-full border-2 border-current border-t-transparent\"\n />\n\n <!-- Wavy spinner (M3 Expressive): the whole shape rotates AND the wave\n travels along the stroke via dashoffset, giving the snake-like flow. -->\n <svg\n v-else\n :width=\"size\"\n :height=\"size\"\n :viewBox=\"`0 0 ${size} ${size}`\"\n fill=\"none\"\n class=\"animate-[m3-wavy-spin_2.8s_linear_infinite]\"\n :style=\"`transform-origin: ${cx}px ${cx}px`\"\n >\n <path\n :d=\"wavyData.path\"\n stroke=\"currentColor\"\n :stroke-width=\"STROKE\"\n stroke-linecap=\"round\"\n :stroke-dasharray=\"wavyData.dash\"\n class=\"animate-[m3-wavy-travel_2s_linear_infinite]\"\n :style=\"{ '--m3-wave-len': wavyData.len }\"\n />\n </svg>\n </span>\n</template>\n\n<style>\n/* The SVG element rotates the whole bumpy circle. */\n@keyframes m3-wavy-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* The stroke's dashoffset slides by one full path length, so the crests\n appear to crawl along the circle — the \"snake\" motion of M3 Expressive.\n Negative direction makes the wave travel forward relative to the spin. */\n@keyframes m3-wavy-travel {\n from {\n stroke-dashoffset: 0;\n }\n to {\n stroke-dashoffset: calc(var(--m3-wave-len) * -1px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .animate-\\[m3-wavy-spin_2\\.8s_linear_infinite\\] {\n animation: m3-wavy-spin 2.8s linear infinite;\n }\n .animate-\\[m3-wavy-travel_2s_linear_infinite\\] {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n size?: number;\n wavy?: boolean;\n }>(),\n { size: 20, wavy: false },\n);\n\nconst STROKE = 3;\nconst BUMPS = 9;\n\n// amp fraction of r = 0.25 → max radius = r * 1.25\n// Constrain so that max_r + STROKE/2 ≤ size/2 - 1 (1px margin from edge)\nconst r = computed(() => (props.size / 2 - 1 - STROKE / 2) / 1.25);\nconst cx = computed(() => props.size / 2);\n\n// Build the full bumpy-circle path and its total length.\nconst wavyData = computed(() => {\n const CX = cx.value;\n const R = r.value;\n const amp = R * 0.08;\n const segs = BUMPS * 24; // smooth curve\n\n const pts: string[] = [];\n let len = 0;\n let px = 0,\n py = 0;\n\n for (let i = 0; i <= segs; i++) {\n const theta = (2 * Math.PI * i) / segs - Math.PI / 2;\n const rr = R + amp * Math.sin(BUMPS * theta);\n const x = CX + rr * Math.cos(theta);\n const y = CX + rr * Math.sin(theta);\n if (i > 0) len += Math.sqrt((x - px) ** 2 + (y - py) ** 2);\n pts.push(`${i === 0 ? \"M\" : \"L\"}${x.toFixed(2)},${y.toFixed(2)}`);\n px = x;\n py = y;\n }\n\n // Visible arc ~58% of the circumference, gap fills the rest.\n const visible = len * 0.58;\n const gap = len - visible;\n const dash = `${visible.toFixed(1)} ${gap.toFixed(1)}`;\n\n // The wave \"travels\" by shifting dashoffset over exactly one full length,\n // so the crests slide around the path independently of the rotation.\n return { path: pts.join(\"\") + \"Z\", dash, len: len.toFixed(1) };\n});\n</script>\n\n<template>\n <span\n class=\"inline-flex shrink-0 items-center justify-center\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n role=\"status\"\n aria-label=\"Cargando\"\n >\n <!-- Standard circular spinner -->\n <span\n v-if=\"!wavy\"\n class=\"block h-full w-full animate-spin rounded-full border-2 border-current border-t-transparent\"\n />\n\n <!-- Wavy spinner (M3 Expressive): the whole shape rotates AND the wave\n travels along the stroke via dashoffset, giving the snake-like flow. -->\n <svg\n v-else\n :width=\"size\"\n :height=\"size\"\n :viewBox=\"`0 0 ${size} ${size}`\"\n fill=\"none\"\n class=\"animate-[m3-wavy-spin_2.8s_linear_infinite]\"\n :style=\"`transform-origin: ${cx}px ${cx}px`\"\n >\n <path\n :d=\"wavyData.path\"\n stroke=\"currentColor\"\n :stroke-width=\"STROKE\"\n stroke-linecap=\"round\"\n :stroke-dasharray=\"wavyData.dash\"\n class=\"animate-[m3-wavy-travel_2s_linear_infinite]\"\n :style=\"{ '--m3-wave-len': wavyData.len }\"\n />\n </svg>\n </span>\n</template>\n\n<style>\n/* The SVG element rotates the whole bumpy circle. */\n@keyframes m3-wavy-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* The stroke's dashoffset slides by one full path length, so the crests\n appear to crawl along the circle — the \"snake\" motion of M3 Expressive.\n Negative direction makes the wave travel forward relative to the spin. */\n@keyframes m3-wavy-travel {\n from {\n stroke-dashoffset: 0;\n }\n to {\n stroke-dashoffset: calc(var(--m3-wave-len) * -1px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .animate-\\[m3-wavy-spin_2\\.8s_linear_infinite\\] {\n animation: m3-wavy-spin 2.8s linear infinite;\n }\n .animate-\\[m3-wavy-travel_2s_linear_infinite\\] {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MSpinner from './MSpinner.vue'\nimport MIcon from './MIcon.vue'\n\nconst NAMED_COLORS = ['primary', 'error'] as const\ntype NamedColor = (typeof NAMED_COLORS)[number]\n\nconst props = withDefaults(\n defineProps<{\n variant?: 'filled' | 'tonal' | 'outlined' | 'text' | 'elevated'\n /**\n * Named semantic color ('primary' | 'error') OR any CSS color string\n * ('red', '#e91e63', 'oklch(0.6 0.2 0)', …).\n * When a CSS color is passed, --color-primary is overridden for this button.\n */\n color?: string\n type?: 'button' | 'submit' | 'reset'\n disabled?: boolean\n loading?: boolean\n icon?: string\n }>(),\n {\n variant: 'filled',\n color: 'primary',\n type: 'button',\n disabled: false,\n loading: false,\n },\n)\n\nconst isCustomColor = computed(\n () => !!props.color && !(NAMED_COLORS as readonly string[]).includes(props.color),\n)\n\nconst customStyle = computed(() => {\n if (!isCustomColor.value) return undefined\n return {\n '--color-primary': props.color,\n '--color-on-primary': '#ffffff',\n '--color-primary-container': props.color + '33',\n '--color-on-primary-container': props.color,\n }\n})\n\nconst isError = computed(() => props.color === 'error')\n\n// State-layer overlay: before: pseudo-element uses currentColor (the button's text color)\n// so it's always the correct M3 state-layer color for every variant automatically.\nconst base =\n 'relative inline-flex items-center justify-center gap-2 h-10 rounded-full text-label-large font-medium ' +\n 'whitespace-nowrap overflow-hidden transition-[box-shadow,background-color,color] duration-150 select-none cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38] disabled:shadow-none ' +\n \"before:content-[''] before:pointer-events-none before:absolute before:inset-0 \" +\n 'before:bg-current before:opacity-0 before:transition-opacity before:duration-150 ' +\n 'enabled:hover:before:opacity-[0.08] enabled:active:before:opacity-[0.12]'\n\nconst variantClasses = computed(() => {\n const err = isError.value\n switch (props.variant) {\n case 'filled':\n return err\n ? 'px-6 bg-error text-on-error enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'px-6 bg-primary text-on-primary enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'tonal':\n return err\n ? 'px-6 bg-error-container text-on-error-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'px-6 bg-secondary-container text-on-secondary-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'elevated':\n return err\n ? 'px-6 bg-surface-container-low text-error shadow-elevation-1 enabled:hover:shadow-elevation-2'\n : 'px-6 bg-surface-container-low text-primary shadow-elevation-1 enabled:hover:shadow-elevation-2'\n case 'outlined':\n return err\n ? 'px-6 border border-error text-error'\n : 'px-6 border border-outline text-primary'\n case 'text':\n return err\n ? 'px-3 text-error'\n : 'px-3 text-primary'\n default:\n return ''\n }\n})\n\nfunction createRipple(event: PointerEvent) {\n if (props.disabled || props.loading) return\n const button = event.currentTarget as HTMLElement\n const rect = button.getBoundingClientRect()\n const d = Math.max(rect.width, rect.height) * 2\n const el = document.createElement('span')\n el.className = 'm3-ripple'\n el.style.cssText = `width:${d}px;height:${d}px;top:${event.clientY - rect.top - d / 2}px;left:${event.clientX - rect.left - d / 2}px`\n button.appendChild(el)\n el.addEventListener('animationend', () => el.remove(), { once: true })\n}\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :class=\"[base, variantClasses]\"\n :style=\"customStyle\"\n @pointerdown=\"createRipple\"\n >\n <MSpinner v-if=\"loading\" :size=\"18\" />\n <MIcon v-else-if=\"icon\" :name=\"icon\" :size=\"20\" />\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MSpinner from './MSpinner.vue'\nimport MIcon from './MIcon.vue'\n\nconst NAMED_COLORS = ['primary', 'error'] as const\ntype NamedColor = (typeof NAMED_COLORS)[number]\n\nconst props = withDefaults(\n defineProps<{\n variant?: 'filled' | 'tonal' | 'outlined' | 'text' | 'elevated'\n /**\n * Named semantic color ('primary' | 'error') OR any CSS color string\n * ('red', '#e91e63', 'oklch(0.6 0.2 0)', …).\n * When a CSS color is passed, --color-primary is overridden for this button.\n */\n color?: string\n type?: 'button' | 'submit' | 'reset'\n disabled?: boolean\n loading?: boolean\n icon?: string\n }>(),\n {\n variant: 'filled',\n color: 'primary',\n type: 'button',\n disabled: false,\n loading: false,\n },\n)\n\nconst isCustomColor = computed(\n () => !!props.color && !(NAMED_COLORS as readonly string[]).includes(props.color),\n)\n\nconst customStyle = computed(() => {\n if (!isCustomColor.value) return undefined\n return {\n '--color-primary': props.color,\n '--color-on-primary': '#ffffff',\n '--color-primary-container': props.color + '33',\n '--color-on-primary-container': props.color,\n }\n})\n\nconst isError = computed(() => props.color === 'error')\n\n// State-layer overlay: before: pseudo-element uses currentColor (the button's text color)\n// so it's always the correct M3 state-layer color for every variant automatically.\nconst base =\n 'relative inline-flex items-center justify-center gap-2 h-10 rounded-full text-label-large font-medium ' +\n 'whitespace-nowrap overflow-hidden transition-[box-shadow,background-color,color] duration-150 select-none cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38] disabled:shadow-none ' +\n \"before:content-[''] before:pointer-events-none before:absolute before:inset-0 \" +\n 'before:bg-current before:opacity-0 before:transition-opacity before:duration-150 ' +\n 'enabled:hover:before:opacity-[0.08] enabled:active:before:opacity-[0.12]'\n\nconst variantClasses = computed(() => {\n const err = isError.value\n switch (props.variant) {\n case 'filled':\n return err\n ? 'px-6 bg-error text-on-error enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'px-6 bg-primary text-on-primary enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'tonal':\n return err\n ? 'px-6 bg-error-container text-on-error-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'px-6 bg-secondary-container text-on-secondary-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'elevated':\n return err\n ? 'px-6 bg-surface-container-low text-error shadow-elevation-1 enabled:hover:shadow-elevation-2'\n : 'px-6 bg-surface-container-low text-primary shadow-elevation-1 enabled:hover:shadow-elevation-2'\n case 'outlined':\n return err\n ? 'px-6 border border-error text-error'\n : 'px-6 border border-outline text-primary'\n case 'text':\n return err\n ? 'px-3 text-error'\n : 'px-3 text-primary'\n default:\n return ''\n }\n})\n\nfunction createRipple(event: PointerEvent) {\n if (props.disabled || props.loading) return\n const button = event.currentTarget as HTMLElement\n const rect = button.getBoundingClientRect()\n const d = Math.max(rect.width, rect.height) * 2\n const el = document.createElement('span')\n el.className = 'm3-ripple'\n el.style.cssText = `width:${d}px;height:${d}px;top:${event.clientY - rect.top - d / 2}px;left:${event.clientX - rect.left - d / 2}px`\n button.appendChild(el)\n el.addEventListener('animationend', () => el.remove(), { once: true })\n}\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :class=\"[base, variantClasses]\"\n :style=\"customStyle\"\n @pointerdown=\"createRipple\"\n >\n <MSpinner v-if=\"loading\" :size=\"18\" />\n <MIcon v-else-if=\"icon\" :name=\"icon\" :size=\"20\" />\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n icon: string\n label: string\n variant?: 'standard' | 'filled' | 'tonal' | 'outlined'\n disabled?: boolean\n size?: number\n }>(),\n {\n variant: 'standard',\n disabled: false,\n size: 40,\n },\n)\n\nconst base =\n 'inline-flex shrink-0 items-center justify-center rounded-full transition-colors duration-150 cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38]'\n\nconst variantClasses = computed(() => {\n switch (props.variant) {\n case 'filled':\n return 'bg-primary text-on-primary hover:shadow-elevation-1'\n case 'tonal':\n return 'bg-secondary-container text-on-secondary-container hover:shadow-elevation-1'\n case 'outlined':\n return 'border border-outline text-on-surface-variant hover:bg-on-surface/8'\n default:\n return 'text-on-surface-variant hover:bg-on-surface/8 active:bg-on-surface/12'\n }\n})\n</script>\n\n<template>\n <button\n type=\"button\"\n :aria-label=\"label\"\n :title=\"label\"\n :disabled=\"disabled\"\n :class=\"[base, variantClasses]\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n >\n <MIcon :name=\"icon\" :size=\"Math.round(size * 0.55)\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n icon: string\n label: string\n variant?: 'standard' | 'filled' | 'tonal' | 'outlined'\n disabled?: boolean\n size?: number\n }>(),\n {\n variant: 'standard',\n disabled: false,\n size: 40,\n },\n)\n\nconst base =\n 'inline-flex shrink-0 items-center justify-center rounded-full transition-colors duration-150 cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38]'\n\nconst variantClasses = computed(() => {\n switch (props.variant) {\n case 'filled':\n return 'bg-primary text-on-primary hover:shadow-elevation-1'\n case 'tonal':\n return 'bg-secondary-container text-on-secondary-container hover:shadow-elevation-1'\n case 'outlined':\n return 'border border-outline text-on-surface-variant hover:bg-on-surface/8'\n default:\n return 'text-on-surface-variant hover:bg-on-surface/8 active:bg-on-surface/12'\n }\n})\n</script>\n\n<template>\n <button\n type=\"button\"\n :aria-label=\"label\"\n :title=\"label\"\n :disabled=\"disabled\"\n :class=\"[base, variantClasses]\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n >\n <MIcon :name=\"icon\" :size=\"Math.round(size * 0.55)\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface CalendarEvent {\n id: string | number\n title: string\n date: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n icon?: string\n}\n\nconst props = withDefaults(defineProps<{\n events?: CalendarEvent[]\n locale?: string\n}>(), { events: () => [], locale: 'es-ES' })\n\nconst emit = defineEmits<{\n dateClick: [string]\n eventClick: [CalendarEvent]\n}>()\n\nconst viewDate = ref(new Date())\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'short' })\n return Array.from({ length: 7 }, (_, i) => f.format(new Date(2024, 0, i + 1)))\n})()\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(viewDate.value)\n)\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`\n}\n\nconst todayIso = fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\ninterface CalendarDay {\n date: number\n iso: string\n current: boolean\n events: CalendarEvent[]\n}\n\nconst calendarDays = computed<CalendarDay[]>(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: CalendarDay[] = []\n\n const eventMap = new Map<string, CalendarEvent[]>()\n for (const ev of props.events) {\n if (!eventMap.has(ev.date)) eventMap.set(ev.date, [])\n eventMap.get(ev.date)!.push(ev)\n }\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, events: eventMap.get(iso) ?? [] })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, events: eventMap.get(iso) ?? [] })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, events: eventMap.get(iso) ?? [] })\n }\n return days\n})\n\nfunction prevMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() - 1); viewDate.value = d }\nfunction nextMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() + 1); viewDate.value = d }\nfunction goToday() { viewDate.value = new Date() }\n\nconst eventColor: Record<string, string> = {\n primary: 'bg-primary text-on-primary',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n error: 'bg-error text-on-error',\n success: 'bg-success text-on-success',\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-3\">\n <div class=\"flex items-center gap-1\">\n <MIconButton icon=\"chevron_left\" label=\"Mes anterior\" :size=\"36\" @click=\"prevMonth\" />\n <MIconButton icon=\"chevron_right\" label=\"Mes siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n <h3 class=\"text-title-medium font-medium capitalize text-on-surface\">{{ monthLabel }}</h3>\n <button\n type=\"button\"\n class=\"cursor-pointer rounded-full border border-outline px-3 py-1 text-label-medium text-on-surface transition-colors hover:bg-on-surface/8\"\n @click=\"goToday\"\n >\n Hoy\n </button>\n </div>\n\n <!-- Weekday headers -->\n <div class=\"grid grid-cols-7 border-b border-outline-variant bg-surface-container-high\">\n <div\n v-for=\"wd in WEEKDAYS\"\n :key=\"wd\"\n class=\"py-2 text-center text-label-small font-medium uppercase text-on-surface-variant\"\n >\n {{ wd }}\n </div>\n </div>\n\n <!-- Days grid -->\n <div class=\"grid grid-cols-7\">\n <div\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n class=\"flex min-h-[80px] cursor-pointer flex-col border-b border-r border-outline-variant/50 p-1.5 transition-colors hover:bg-on-surface/[0.03]\"\n :class=\"[\n !day.current ? 'bg-surface-container-lowest/50' : 'bg-surface',\n (i + 1) % 7 === 0 ? 'border-r-0' : '',\n i >= 35 ? 'border-b-0' : '',\n ]\"\n @click=\"emit('dateClick', day.iso)\"\n >\n <!-- Day number -->\n <span\n class=\"mb-0.5 flex h-6 w-6 items-center justify-center self-end rounded-full text-label-medium\"\n :class=\"\n day.iso === todayIso\n ? 'bg-primary text-on-primary font-medium'\n : day.current\n ? 'text-on-surface'\n : 'text-on-surface-variant/40'\n \"\n >\n {{ day.date }}\n </span>\n\n <!-- Events -->\n <div v-if=\"day.events.length\" class=\"flex flex-col gap-0.5\">\n <button\n v-for=\"ev in day.events.slice(0, 2)\"\n :key=\"ev.id\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-1 truncate rounded px-1 py-0.5 text-left text-label-small transition-opacity hover:opacity-80\"\n :class=\"eventColor[ev.color ?? 'primary']\"\n @click.stop=\"emit('eventClick', ev)\"\n >\n <MIcon v-if=\"ev.icon\" :name=\"ev.icon\" :size=\"12\" />\n <span class=\"truncate\">{{ ev.title }}</span>\n </button>\n <span\n v-if=\"day.events.length > 2\"\n class=\"px-1 text-label-small text-on-surface-variant\"\n >\n +{{ day.events.length - 2 }} más\n </span>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface CalendarEvent {\n id: string | number\n title: string\n date: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n icon?: string\n}\n\nconst props = withDefaults(defineProps<{\n events?: CalendarEvent[]\n locale?: string\n}>(), { events: () => [], locale: 'es-ES' })\n\nconst emit = defineEmits<{\n dateClick: [string]\n eventClick: [CalendarEvent]\n}>()\n\nconst viewDate = ref(new Date())\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'short' })\n return Array.from({ length: 7 }, (_, i) => f.format(new Date(2024, 0, i + 1)))\n})()\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(viewDate.value)\n)\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`\n}\n\nconst todayIso = fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\ninterface CalendarDay {\n date: number\n iso: string\n current: boolean\n events: CalendarEvent[]\n}\n\nconst calendarDays = computed<CalendarDay[]>(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: CalendarDay[] = []\n\n const eventMap = new Map<string, CalendarEvent[]>()\n for (const ev of props.events) {\n if (!eventMap.has(ev.date)) eventMap.set(ev.date, [])\n eventMap.get(ev.date)!.push(ev)\n }\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, events: eventMap.get(iso) ?? [] })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, events: eventMap.get(iso) ?? [] })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, events: eventMap.get(iso) ?? [] })\n }\n return days\n})\n\nfunction prevMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() - 1); viewDate.value = d }\nfunction nextMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() + 1); viewDate.value = d }\nfunction goToday() { viewDate.value = new Date() }\n\nconst eventColor: Record<string, string> = {\n primary: 'bg-primary text-on-primary',\n secondary: 'bg-secondary text-on-secondary',\n tertiary: 'bg-tertiary text-on-tertiary',\n error: 'bg-error text-on-error',\n success: 'bg-success text-on-success',\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-3\">\n <div class=\"flex items-center gap-1\">\n <MIconButton icon=\"chevron_left\" label=\"Mes anterior\" :size=\"36\" @click=\"prevMonth\" />\n <MIconButton icon=\"chevron_right\" label=\"Mes siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n <h3 class=\"text-title-medium font-medium capitalize text-on-surface\">{{ monthLabel }}</h3>\n <button\n type=\"button\"\n class=\"cursor-pointer rounded-full border border-outline px-3 py-1 text-label-medium text-on-surface transition-colors hover:bg-on-surface/8\"\n @click=\"goToday\"\n >\n Hoy\n </button>\n </div>\n\n <!-- Weekday headers -->\n <div class=\"grid grid-cols-7 border-b border-outline-variant bg-surface-container-high\">\n <div\n v-for=\"wd in WEEKDAYS\"\n :key=\"wd\"\n class=\"py-2 text-center text-label-small font-medium uppercase text-on-surface-variant\"\n >\n {{ wd }}\n </div>\n </div>\n\n <!-- Days grid -->\n <div class=\"grid grid-cols-7\">\n <div\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n class=\"flex min-h-[80px] cursor-pointer flex-col border-b border-r border-outline-variant/50 p-1.5 transition-colors hover:bg-on-surface/[0.03]\"\n :class=\"[\n !day.current ? 'bg-surface-container-lowest/50' : 'bg-surface',\n (i + 1) % 7 === 0 ? 'border-r-0' : '',\n i >= 35 ? 'border-b-0' : '',\n ]\"\n @click=\"emit('dateClick', day.iso)\"\n >\n <!-- Day number -->\n <span\n class=\"mb-0.5 flex h-6 w-6 items-center justify-center self-end rounded-full text-label-medium\"\n :class=\"\n day.iso === todayIso\n ? 'bg-primary text-on-primary font-medium'\n : day.current\n ? 'text-on-surface'\n : 'text-on-surface-variant/40'\n \"\n >\n {{ day.date }}\n </span>\n\n <!-- Events -->\n <div v-if=\"day.events.length\" class=\"flex flex-col gap-0.5\">\n <button\n v-for=\"ev in day.events.slice(0, 2)\"\n :key=\"ev.id\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-1 truncate rounded px-1 py-0.5 text-left text-label-small transition-opacity hover:opacity-80\"\n :class=\"eventColor[ev.color ?? 'primary']\"\n @click.stop=\"emit('eventClick', ev)\"\n >\n <MIcon v-if=\"ev.icon\" :name=\"ev.icon\" :size=\"12\" />\n <span class=\"truncate\">{{ ev.title }}</span>\n </button>\n <span\n v-if=\"day.events.length > 2\"\n class=\"px-1 text-label-small text-on-surface-variant\"\n >\n +{{ day.events.length - 2 }} más\n </span>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n variant?: 'elevated' | 'filled' | 'outlined'\n clickable?: boolean\n elevated?: boolean\n /** src URL for a full-bleed header image */\n image?: string\n imageAlt?: string\n imageHeight?: string\n }>(),\n { variant: 'elevated', clickable: false, elevated: false },\n)\n\nconst resolvedVariant = computed(() => (props.elevated ? 'elevated' : props.variant))\n\nconst variantClasses: Record<string, string> = {\n elevated: 'bg-surface-container-low shadow-elevation-1',\n filled: 'bg-surface-container-highest',\n outlined: 'bg-surface border border-outline-variant',\n}\n\n// Expose the card's background as --field-bg so outlined text-field labels\n// inside the card automatically match without needing the fieldBg prop.\nconst fieldBgByVariant: Record<string, string> = {\n elevated: 'var(--color-surface-container-low)',\n filled: 'var(--color-surface-container-highest)',\n outlined: 'var(--color-surface)',\n}\n</script>\n\n<template>\n <div\n class=\"overflow-hidden rounded-md transition-shadow duration-150\"\n :class=\"[\n variantClasses[resolvedVariant],\n clickable ? 'cursor-pointer hover:shadow-elevation-2 active:shadow-elevation-1' : '',\n ]\"\n :style=\"{ '--field-bg': fieldBgByVariant[resolvedVariant] }\"\n >\n <!-- Optional header image -->\n <div v-if=\"image || $slots.media\" :class=\"['w-full overflow-hidden', imageHeight ?? 'h-48']\">\n <img\n v-if=\"image\"\n :src=\"image\"\n :alt=\"imageAlt ?? ''\"\n class=\"h-full w-full object-cover\"\n />\n <slot v-else name=\"media\" />\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n variant?: 'elevated' | 'filled' | 'outlined'\n clickable?: boolean\n elevated?: boolean\n /** src URL for a full-bleed header image */\n image?: string\n imageAlt?: string\n imageHeight?: string\n }>(),\n { variant: 'elevated', clickable: false, elevated: false },\n)\n\nconst resolvedVariant = computed(() => (props.elevated ? 'elevated' : props.variant))\n\nconst variantClasses: Record<string, string> = {\n elevated: 'bg-surface-container-low shadow-elevation-1',\n filled: 'bg-surface-container-highest',\n outlined: 'bg-surface border border-outline-variant',\n}\n\n// Expose the card's background as --field-bg so outlined text-field labels\n// inside the card automatically match without needing the fieldBg prop.\nconst fieldBgByVariant: Record<string, string> = {\n elevated: 'var(--color-surface-container-low)',\n filled: 'var(--color-surface-container-highest)',\n outlined: 'var(--color-surface)',\n}\n</script>\n\n<template>\n <div\n class=\"overflow-hidden rounded-md transition-shadow duration-150\"\n :class=\"[\n variantClasses[resolvedVariant],\n clickable ? 'cursor-pointer hover:shadow-elevation-2 active:shadow-elevation-1' : '',\n ]\"\n :style=\"{ '--field-bg': fieldBgByVariant[resolvedVariant] }\"\n >\n <!-- Optional header image -->\n <div v-if=\"image || $slots.media\" :class=\"['w-full overflow-hidden', imageHeight ?? 'h-48']\">\n <img\n v-if=\"image\"\n :src=\"image\"\n :alt=\"imageAlt ?? ''\"\n class=\"h-full w-full object-cover\"\n />\n <slot v-else name=\"media\" />\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from \"./MIcon.vue\";\n\nwithDefaults(\n defineProps<{\n modelValue: boolean;\n indeterminate?: boolean;\n disabled?: boolean;\n label?: string;\n }>(),\n { indeterminate: false, disabled: false },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [boolean] }>();\n</script>\n\n<template>\n <label\n class=\"inline-flex items-center gap-2 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span\n class=\"relative inline-flex h-4.5 w-4.5 shrink-0 items-center justify-center rounded-[3px] border-2 transition-colors\"\n :class=\"\n modelValue || indeterminate\n ? 'border-primary bg-primary text-on-primary'\n : 'border-on-surface-variant text-transparent'\n \"\n >\n <input\n type=\"checkbox\"\n class=\"sr-only\"\n :checked=\"modelValue\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', !modelValue)\"\n />\n <MIcon\n :name=\"indeterminate ? 'remove' : 'check'\"\n :size=\"14\"\n class=\"transition-[opacity,transform] duration-150\"\n :class=\"modelValue || indeterminate ? 'scale-100 opacity-100' : 'scale-0 opacity-0'\"\n />\n </span>\n <span v-if=\"label || $slots.default\" class=\"text-body-large text-on-surface\">\n <slot>{{ label }}</slot>\n </span>\n </label>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from \"./MIcon.vue\";\n\nwithDefaults(\n defineProps<{\n modelValue: boolean;\n indeterminate?: boolean;\n disabled?: boolean;\n label?: string;\n }>(),\n { indeterminate: false, disabled: false },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [boolean] }>();\n</script>\n\n<template>\n <label\n class=\"inline-flex items-center gap-2 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span\n class=\"relative inline-flex h-4.5 w-4.5 shrink-0 items-center justify-center rounded-[3px] border-2 transition-colors\"\n :class=\"\n modelValue || indeterminate\n ? 'border-primary bg-primary text-on-primary'\n : 'border-on-surface-variant text-transparent'\n \"\n >\n <input\n type=\"checkbox\"\n class=\"sr-only\"\n :checked=\"modelValue\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', !modelValue)\"\n />\n <MIcon\n :name=\"indeterminate ? 'remove' : 'check'\"\n :size=\"14\"\n class=\"transition-[opacity,transform] duration-150\"\n :class=\"modelValue || indeterminate ? 'scale-100 opacity-100' : 'scale-0 opacity-0'\"\n />\n </span>\n <span v-if=\"label || $slots.default\" class=\"text-body-large text-on-surface\">\n <slot>{{ label }}</slot>\n </span>\n </label>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst NAMED_TONES = ['neutral', 'primary', 'success', 'error', 'tertiary', 'secondary'] as const\ntype NamedTone = (typeof NAMED_TONES)[number]\n\nconst props = withDefaults(\n defineProps<{\n tone?: string // named tone OR CSS color string\n selected?: boolean\n removable?: boolean\n clickable?: boolean\n disabled?: boolean\n icon?: string\n }>(),\n {\n tone: 'neutral',\n selected: false,\n removable: false,\n clickable: false,\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{ click: []; remove: [] }>()\n\nconst isCustomColor = computed(\n () => !!props.tone && !(NAMED_TONES as readonly string[]).includes(props.tone),\n)\n\n// When a CSS color is passed, apply it via CSS variables\nconst customStyle = computed(() => {\n if (!isCustomColor.value) return undefined\n return {\n '--chip-bg': props.tone + '22',\n '--chip-color': props.tone,\n }\n})\n\nconst toneClasses = computed(() => {\n if (isCustomColor.value) {\n return 'border border-transparent bg-[var(--chip-bg)] text-[var(--chip-color)]'\n }\n if (props.tone === 'neutral' && !props.selected) {\n return 'border border-outline bg-transparent text-on-surface-variant'\n }\n const map: Record<string, string> = {\n neutral: 'border border-transparent bg-secondary-container text-on-secondary-container',\n primary: 'border border-transparent bg-primary-container text-on-primary-container',\n secondary: 'border border-transparent bg-secondary-container text-on-secondary-container',\n success: 'border border-transparent bg-success-container text-on-success-container',\n error: 'border border-transparent bg-error-container text-on-error-container',\n tertiary: 'border border-transparent bg-tertiary-container text-on-tertiary-container',\n }\n return map[props.tone ?? 'neutral'] ?? map.neutral\n})\n</script>\n\n<template>\n <component\n :is=\"clickable ? 'button' : 'span'\"\n :type=\"clickable ? 'button' : undefined\"\n :disabled=\"clickable && disabled ? true : undefined\"\n class=\"inline-flex h-8 items-center gap-1.5 rounded-sm px-3 text-label-large transition-colors\"\n :class=\"[\n toneClasses,\n clickable && !disabled ? 'cursor-pointer hover:bg-on-surface/8' : '',\n disabled ? 'cursor-not-allowed opacity-[0.38]' : '',\n ]\"\n :style=\"customStyle\"\n @click=\"clickable && !disabled && emit('click')\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"18\" />\n <slot />\n <button\n v-if=\"removable\"\n type=\"button\"\n class=\"-mr-1 ml-0.5 inline-flex items-center justify-center rounded-full hover:bg-on-surface/12\"\n aria-label=\"Quitar\"\n :disabled=\"disabled\"\n @click.stop=\"emit('remove')\"\n >\n <MIcon name=\"close\" :size=\"16\" />\n </button>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst NAMED_TONES = ['neutral', 'primary', 'success', 'error', 'tertiary', 'secondary'] as const\ntype NamedTone = (typeof NAMED_TONES)[number]\n\nconst props = withDefaults(\n defineProps<{\n tone?: string // named tone OR CSS color string\n selected?: boolean\n removable?: boolean\n clickable?: boolean\n disabled?: boolean\n icon?: string\n }>(),\n {\n tone: 'neutral',\n selected: false,\n removable: false,\n clickable: false,\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{ click: []; remove: [] }>()\n\nconst isCustomColor = computed(\n () => !!props.tone && !(NAMED_TONES as readonly string[]).includes(props.tone),\n)\n\n// When a CSS color is passed, apply it via CSS variables\nconst customStyle = computed(() => {\n if (!isCustomColor.value) return undefined\n return {\n '--chip-bg': props.tone + '22',\n '--chip-color': props.tone,\n }\n})\n\nconst toneClasses = computed(() => {\n if (isCustomColor.value) {\n return 'border border-transparent bg-[var(--chip-bg)] text-[var(--chip-color)]'\n }\n if (props.tone === 'neutral' && !props.selected) {\n return 'border border-outline bg-transparent text-on-surface-variant'\n }\n const map: Record<string, string> = {\n neutral: 'border border-transparent bg-secondary-container text-on-secondary-container',\n primary: 'border border-transparent bg-primary-container text-on-primary-container',\n secondary: 'border border-transparent bg-secondary-container text-on-secondary-container',\n success: 'border border-transparent bg-success-container text-on-success-container',\n error: 'border border-transparent bg-error-container text-on-error-container',\n tertiary: 'border border-transparent bg-tertiary-container text-on-tertiary-container',\n }\n return map[props.tone ?? 'neutral'] ?? map.neutral\n})\n</script>\n\n<template>\n <component\n :is=\"clickable ? 'button' : 'span'\"\n :type=\"clickable ? 'button' : undefined\"\n :disabled=\"clickable && disabled ? true : undefined\"\n class=\"inline-flex h-8 items-center gap-1.5 rounded-sm px-3 text-label-large transition-colors\"\n :class=\"[\n toneClasses,\n clickable && !disabled ? 'cursor-pointer hover:bg-on-surface/8' : '',\n disabled ? 'cursor-not-allowed opacity-[0.38]' : '',\n ]\"\n :style=\"customStyle\"\n @click=\"clickable && !disabled && emit('click')\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"18\" />\n <slot />\n <button\n v-if=\"removable\"\n type=\"button\"\n class=\"-mr-1 ml-0.5 inline-flex items-center justify-center rounded-full hover:bg-on-surface/12\"\n aria-label=\"Quitar\"\n :disabled=\"disabled\"\n @click.stop=\"emit('remove')\"\n >\n <MIcon name=\"close\" :size=\"16\" />\n </button>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string\n label?: string\n presets?: string[]\n disabled?: boolean\n error?: string\n hint?: string\n fieldBg?: string\n}>(), {\n presets: () => [\n '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3',\n '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39',\n '#ffeb3b', '#ffc107', '#ff9800', '#ff5722', '#795548', '#607d8b',\n ],\n})\n\nconst emit = defineEmits<{ 'update:modelValue': [string] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst satBrightEl = ref<HTMLElement>()\nconst draggingSB = ref(false)\n\n// HSV state\nconst hue = ref(0)\nconst sat = ref(100)\nconst bright = ref(100)\n\nfunction hexToHsv(hex: string) {\n let c = hex.replace('#', '')\n if (c.length === 3) c = c[0]!+c[0]!+c[1]!+c[1]!+c[2]!+c[2]!\n const r = parseInt(c.substring(0, 2), 16) / 255\n const g = parseInt(c.substring(2, 4), 16) / 255\n const b = parseInt(c.substring(4, 6), 16) / 255\n const max = Math.max(r, g, b), min = Math.min(r, g, b)\n const d = max - min\n let h = 0\n if (d !== 0) {\n if (max === r) h = ((g - b) / d + 6) % 6\n else if (max === g) h = (b - r) / d + 2\n else h = (r - g) / d + 4\n h *= 60\n }\n const s = max === 0 ? 0 : (d / max) * 100\n const v = max * 100\n return { h, s, v }\n}\n\nfunction hsvToHex(h: number, s: number, v: number) {\n s /= 100; v /= 100\n const c = v * s\n const x = c * (1 - Math.abs((h / 60) % 2 - 1))\n const m = v - c\n let r = 0, g = 0, b = 0\n if (h < 60) { r = c; g = x }\n else if (h < 120) { r = x; g = c }\n else if (h < 180) { g = c; b = x }\n else if (h < 240) { g = x; b = c }\n else if (h < 300) { r = x; b = c }\n else { r = c; b = x }\n const toHex = (n: number) => Math.round((n + m) * 255).toString(16).padStart(2, '0')\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`\n}\n\nfunction syncFromProp() {\n const hsv = hexToHsv(props.modelValue)\n hue.value = hsv.h\n sat.value = hsv.s\n bright.value = hsv.v\n}\nsyncFromProp()\n\nwatch(() => props.modelValue, syncFromProp)\n\nfunction emitColor() {\n emit('update:modelValue', hsvToHex(hue.value, sat.value, bright.value))\n}\n\nconst currentHex = computed(() => hsvToHex(hue.value, sat.value, bright.value))\nconst hueColor = computed(() => `hsl(${hue.value}, 100%, 50%)`)\n\nfunction onSBPointerDown(e: PointerEvent) {\n draggingSB.value = true\n updateSB(e)\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onSBPointerMove(e: PointerEvent) {\n if (!draggingSB.value) return\n updateSB(e)\n}\nfunction onSBPointerUp() {\n draggingSB.value = false\n emitColor()\n}\nfunction updateSB(e: PointerEvent) {\n if (!satBrightEl.value) return\n const rect = satBrightEl.value.getBoundingClientRect()\n sat.value = Math.max(0, Math.min(100, ((e.clientX - rect.left) / rect.width) * 100))\n bright.value = Math.max(0, Math.min(100, (1 - (e.clientY - rect.top) / rect.height) * 100))\n}\n\nfunction onHueInput(e: Event) {\n hue.value = Number((e.target as HTMLInputElement).value)\n emitColor()\n}\n\nfunction selectPreset(color: string) {\n emit('update:modelValue', color)\n}\n\nfunction onHexInput(e: Event) {\n const v = (e.target as HTMLInputElement).value\n if (/^#[0-9a-fA-F]{6}$/.test(v)) {\n emit('update:modelValue', v)\n }\n}\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 380\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-3 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <span\n class=\"h-6 w-6 shrink-0 rounded-full border border-outline-variant\"\n :style=\"{ backgroundColor: modelValue }\"\n />\n <span class=\"flex-1 font-mono text-on-surface\">{{ modelValue }}</span>\n <MIcon name=\"palette\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[280px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Saturation / Brightness area -->\n <div\n ref=\"satBrightEl\"\n class=\"relative mb-3 h-40 w-full cursor-crosshair overflow-hidden rounded-lg\"\n :style=\"{ background: `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${hueColor})` }\"\n @pointerdown=\"onSBPointerDown\"\n @pointermove=\"onSBPointerMove\"\n @pointerup=\"onSBPointerUp\"\n >\n <div\n class=\"pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-elevation-1\"\n :style=\"{\n left: `${sat}%`,\n top: `${100 - bright}%`,\n backgroundColor: currentHex,\n }\"\n />\n </div>\n\n <!-- Hue slider -->\n <div class=\"mb-3\">\n <input\n type=\"range\"\n min=\"0\"\n max=\"360\"\n :value=\"hue\"\n class=\"hue-slider h-3 w-full cursor-pointer appearance-none rounded-full outline-none\"\n @input=\"onHueInput\"\n />\n </div>\n\n <!-- Preview + hex input -->\n <div class=\"mb-3 flex items-center gap-3\">\n <span\n class=\"h-9 w-9 shrink-0 rounded-full border border-outline-variant\"\n :style=\"{ backgroundColor: currentHex }\"\n />\n <input\n type=\"text\"\n :value=\"modelValue\"\n maxlength=\"7\"\n class=\"flex-1 rounded-sm border border-outline bg-transparent px-3 py-2 font-mono text-body-medium text-on-surface outline-none transition-colors focus:border-primary\"\n @input=\"onHexInput\"\n />\n </div>\n\n <!-- Presets -->\n <div class=\"flex flex-wrap gap-1.5\">\n <button\n v-for=\"color in presets\"\n :key=\"color\"\n type=\"button\"\n class=\"flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border transition-transform duration-100 hover:scale-110\"\n :class=\"color === modelValue ? 'border-on-surface' : 'border-transparent'\"\n :style=\"{ backgroundColor: color }\"\n @click=\"selectPreset(color)\"\n >\n <MIcon v-if=\"color === modelValue\" name=\"check\" :size=\"14\" class=\"text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.5)]\" />\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<style scoped>\n.hue-slider {\n background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);\n}\n.hue-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n cursor: pointer;\n}\n.hue-slider::-moz-range-thumb {\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n border: none;\n cursor: pointer;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string\n label?: string\n presets?: string[]\n disabled?: boolean\n error?: string\n hint?: string\n fieldBg?: string\n}>(), {\n presets: () => [\n '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3',\n '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39',\n '#ffeb3b', '#ffc107', '#ff9800', '#ff5722', '#795548', '#607d8b',\n ],\n})\n\nconst emit = defineEmits<{ 'update:modelValue': [string] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst satBrightEl = ref<HTMLElement>()\nconst draggingSB = ref(false)\n\n// HSV state\nconst hue = ref(0)\nconst sat = ref(100)\nconst bright = ref(100)\n\nfunction hexToHsv(hex: string) {\n let c = hex.replace('#', '')\n if (c.length === 3) c = c[0]!+c[0]!+c[1]!+c[1]!+c[2]!+c[2]!\n const r = parseInt(c.substring(0, 2), 16) / 255\n const g = parseInt(c.substring(2, 4), 16) / 255\n const b = parseInt(c.substring(4, 6), 16) / 255\n const max = Math.max(r, g, b), min = Math.min(r, g, b)\n const d = max - min\n let h = 0\n if (d !== 0) {\n if (max === r) h = ((g - b) / d + 6) % 6\n else if (max === g) h = (b - r) / d + 2\n else h = (r - g) / d + 4\n h *= 60\n }\n const s = max === 0 ? 0 : (d / max) * 100\n const v = max * 100\n return { h, s, v }\n}\n\nfunction hsvToHex(h: number, s: number, v: number) {\n s /= 100; v /= 100\n const c = v * s\n const x = c * (1 - Math.abs((h / 60) % 2 - 1))\n const m = v - c\n let r = 0, g = 0, b = 0\n if (h < 60) { r = c; g = x }\n else if (h < 120) { r = x; g = c }\n else if (h < 180) { g = c; b = x }\n else if (h < 240) { g = x; b = c }\n else if (h < 300) { r = x; b = c }\n else { r = c; b = x }\n const toHex = (n: number) => Math.round((n + m) * 255).toString(16).padStart(2, '0')\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`\n}\n\nfunction syncFromProp() {\n const hsv = hexToHsv(props.modelValue)\n hue.value = hsv.h\n sat.value = hsv.s\n bright.value = hsv.v\n}\nsyncFromProp()\n\nwatch(() => props.modelValue, syncFromProp)\n\nfunction emitColor() {\n emit('update:modelValue', hsvToHex(hue.value, sat.value, bright.value))\n}\n\nconst currentHex = computed(() => hsvToHex(hue.value, sat.value, bright.value))\nconst hueColor = computed(() => `hsl(${hue.value}, 100%, 50%)`)\n\nfunction onSBPointerDown(e: PointerEvent) {\n draggingSB.value = true\n updateSB(e)\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onSBPointerMove(e: PointerEvent) {\n if (!draggingSB.value) return\n updateSB(e)\n}\nfunction onSBPointerUp() {\n draggingSB.value = false\n emitColor()\n}\nfunction updateSB(e: PointerEvent) {\n if (!satBrightEl.value) return\n const rect = satBrightEl.value.getBoundingClientRect()\n sat.value = Math.max(0, Math.min(100, ((e.clientX - rect.left) / rect.width) * 100))\n bright.value = Math.max(0, Math.min(100, (1 - (e.clientY - rect.top) / rect.height) * 100))\n}\n\nfunction onHueInput(e: Event) {\n hue.value = Number((e.target as HTMLInputElement).value)\n emitColor()\n}\n\nfunction selectPreset(color: string) {\n emit('update:modelValue', color)\n}\n\nfunction onHexInput(e: Event) {\n const v = (e.target as HTMLInputElement).value\n if (/^#[0-9a-fA-F]{6}$/.test(v)) {\n emit('update:modelValue', v)\n }\n}\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 380\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-3 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <span\n class=\"h-6 w-6 shrink-0 rounded-full border border-outline-variant\"\n :style=\"{ backgroundColor: modelValue }\"\n />\n <span class=\"flex-1 font-mono text-on-surface\">{{ modelValue }}</span>\n <MIcon name=\"palette\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[280px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Saturation / Brightness area -->\n <div\n ref=\"satBrightEl\"\n class=\"relative mb-3 h-40 w-full cursor-crosshair overflow-hidden rounded-lg\"\n :style=\"{ background: `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${hueColor})` }\"\n @pointerdown=\"onSBPointerDown\"\n @pointermove=\"onSBPointerMove\"\n @pointerup=\"onSBPointerUp\"\n >\n <div\n class=\"pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-elevation-1\"\n :style=\"{\n left: `${sat}%`,\n top: `${100 - bright}%`,\n backgroundColor: currentHex,\n }\"\n />\n </div>\n\n <!-- Hue slider -->\n <div class=\"mb-3\">\n <input\n type=\"range\"\n min=\"0\"\n max=\"360\"\n :value=\"hue\"\n class=\"hue-slider h-3 w-full cursor-pointer appearance-none rounded-full outline-none\"\n @input=\"onHueInput\"\n />\n </div>\n\n <!-- Preview + hex input -->\n <div class=\"mb-3 flex items-center gap-3\">\n <span\n class=\"h-9 w-9 shrink-0 rounded-full border border-outline-variant\"\n :style=\"{ backgroundColor: currentHex }\"\n />\n <input\n type=\"text\"\n :value=\"modelValue\"\n maxlength=\"7\"\n class=\"flex-1 rounded-sm border border-outline bg-transparent px-3 py-2 font-mono text-body-medium text-on-surface outline-none transition-colors focus:border-primary\"\n @input=\"onHexInput\"\n />\n </div>\n\n <!-- Presets -->\n <div class=\"flex flex-wrap gap-1.5\">\n <button\n v-for=\"color in presets\"\n :key=\"color\"\n type=\"button\"\n class=\"flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border transition-transform duration-100 hover:scale-110\"\n :class=\"color === modelValue ? 'border-on-surface' : 'border-transparent'\"\n :style=\"{ backgroundColor: color }\"\n @click=\"selectPreset(color)\"\n >\n <MIcon v-if=\"color === modelValue\" name=\"check\" :size=\"14\" class=\"text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.5)]\" />\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<style scoped>\n.hue-slider {\n background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);\n}\n.hue-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n cursor: pointer;\n}\n.hue-slider::-moz-range-thumb {\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n border: none;\n cursor: pointer;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface CommandItem {\n id: string\n label: string\n icon?: string\n shortcut?: string\n group?: string\n disabled?: boolean\n onSelect?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n items: CommandItem[]\n placeholder?: string\n noResultsText?: string\n hotkey?: string\n }>(),\n {\n placeholder: 'Buscar comando...',\n noResultsText: 'Sin resultados',\n hotkey: 'k',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n select: [CommandItem]\n}>()\n\nconst query = ref('')\nconst activeIndex = ref(0)\nconst inputRef = ref<HTMLInputElement | null>(null)\n\nconst filtered = computed(() => {\n if (!query.value) return props.items.filter(i => !i.disabled)\n const q = query.value.toLowerCase()\n return props.items.filter(\n i => !i.disabled && (i.label.toLowerCase().includes(q) || i.group?.toLowerCase().includes(q)),\n )\n})\n\nconst grouped = computed(() => {\n const map = new Map<string, CommandItem[]>()\n for (const item of filtered.value) {\n const g = item.group ?? ''\n if (!map.has(g)) map.set(g, [])\n map.get(g)!.push(item)\n }\n return map\n})\n\nfunction open() {\n emit('update:modelValue', true)\n}\n\nfunction close() {\n query.value = ''\n activeIndex.value = 0\n emit('update:modelValue', false)\n}\n\nfunction selectItem(item: CommandItem) {\n emit('select', item)\n item.onSelect?.()\n close()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n activeIndex.value = (activeIndex.value + 1) % filtered.value.length\n scrollToActive()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n activeIndex.value = (activeIndex.value - 1 + filtered.value.length) % filtered.value.length\n scrollToActive()\n } else if (e.key === 'Enter' && filtered.value.length) {\n e.preventDefault()\n selectItem(filtered.value[activeIndex.value]!)\n } else if (e.key === 'Escape') {\n close()\n }\n}\n\nfunction scrollToActive() {\n nextTick(() => {\n const el = document.querySelector('[data-cmd-active=\"true\"]')\n el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction onGlobalKeydown(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === props.hotkey) {\n e.preventDefault()\n if (props.modelValue) close()\n else open()\n }\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.body.style.overflow = 'hidden'\n nextTick(() => inputRef.value?.focus())\n } else {\n document.body.style.overflow = ''\n }\n },\n)\n\nwatch(query, () => { activeIndex.value = 0 })\n\nonMounted(() => document.addEventListener('keydown', onGlobalKeydown))\nonBeforeUnmount(() => document.removeEventListener('keydown', onGlobalKeydown))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-cmd\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-black/40 pt-[15vh]\"\n @click.self=\"close\"\n >\n <div class=\"cmd-box flex w-full max-w-lg flex-col overflow-hidden rounded-xl bg-surface-container-high shadow-elevation-3\">\n <!-- Search input -->\n <div class=\"flex items-center gap-3 border-b border-outline-variant px-4\">\n <MIcon name=\"search\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n ref=\"inputRef\"\n v-model=\"query\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"h-12 flex-1 bg-transparent text-body-large text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n @keydown=\"onKeydown\"\n />\n <kbd class=\"rounded bg-surface-container px-1.5 py-0.5 text-label-small text-on-surface-variant\">\n ESC\n </kbd>\n </div>\n\n <!-- Results -->\n <div class=\"max-h-80 overflow-y-auto py-2\">\n <template v-if=\"filtered.length\">\n <template v-for=\"[group, items] in grouped\" :key=\"group\">\n <p v-if=\"group\" class=\"px-4 pt-3 pb-1 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ group }}\n </p>\n <button\n v-for=\"(item, i) in items\"\n :key=\"item.id\"\n type=\"button\"\n :data-cmd-active=\"filtered.indexOf(item) === activeIndex || undefined\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left transition-colors\"\n :class=\"filtered.indexOf(item) === activeIndex ? 'bg-primary/12 text-primary' : 'text-on-surface hover:bg-on-surface/4'\"\n @click=\"selectItem(item)\"\n @pointerenter=\"activeIndex = filtered.indexOf(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"20\" class=\"shrink-0 opacity-70\" />\n <span class=\"flex-1 truncate text-body-medium\">{{ item.label }}</span>\n <kbd v-if=\"item.shortcut\" class=\"rounded bg-surface-container px-1.5 py-0.5 text-label-small text-on-surface-variant\">\n {{ item.shortcut }}\n </kbd>\n </button>\n </template>\n </template>\n <p v-else class=\"px-4 py-6 text-center text-body-medium text-on-surface-variant\">\n {{ noResultsText }}\n </p>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center gap-4 border-t border-outline-variant px-4 py-2\">\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↑↓</kbd> navegar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↵</kbd> seleccionar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">esc</kbd> cerrar\n </span>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-cmd-enter-active,\n.m3-cmd-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-cmd-enter-from,\n.m3-cmd-leave-to {\n opacity: 0;\n}\n.m3-cmd-enter-active .cmd-box,\n.m3-cmd-leave-active .cmd-box {\n transition: transform 0.15s ease;\n}\n.m3-cmd-enter-from .cmd-box,\n.m3-cmd-leave-to .cmd-box {\n transform: scale(0.95) translateY(-10px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface CommandItem {\n id: string\n label: string\n icon?: string\n shortcut?: string\n group?: string\n disabled?: boolean\n onSelect?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n items: CommandItem[]\n placeholder?: string\n noResultsText?: string\n hotkey?: string\n }>(),\n {\n placeholder: 'Buscar comando...',\n noResultsText: 'Sin resultados',\n hotkey: 'k',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n select: [CommandItem]\n}>()\n\nconst query = ref('')\nconst activeIndex = ref(0)\nconst inputRef = ref<HTMLInputElement | null>(null)\n\nconst filtered = computed(() => {\n if (!query.value) return props.items.filter(i => !i.disabled)\n const q = query.value.toLowerCase()\n return props.items.filter(\n i => !i.disabled && (i.label.toLowerCase().includes(q) || i.group?.toLowerCase().includes(q)),\n )\n})\n\nconst grouped = computed(() => {\n const map = new Map<string, CommandItem[]>()\n for (const item of filtered.value) {\n const g = item.group ?? ''\n if (!map.has(g)) map.set(g, [])\n map.get(g)!.push(item)\n }\n return map\n})\n\nfunction open() {\n emit('update:modelValue', true)\n}\n\nfunction close() {\n query.value = ''\n activeIndex.value = 0\n emit('update:modelValue', false)\n}\n\nfunction selectItem(item: CommandItem) {\n emit('select', item)\n item.onSelect?.()\n close()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n activeIndex.value = (activeIndex.value + 1) % filtered.value.length\n scrollToActive()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n activeIndex.value = (activeIndex.value - 1 + filtered.value.length) % filtered.value.length\n scrollToActive()\n } else if (e.key === 'Enter' && filtered.value.length) {\n e.preventDefault()\n selectItem(filtered.value[activeIndex.value]!)\n } else if (e.key === 'Escape') {\n close()\n }\n}\n\nfunction scrollToActive() {\n nextTick(() => {\n const el = document.querySelector('[data-cmd-active=\"true\"]')\n el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction onGlobalKeydown(e: KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key === props.hotkey) {\n e.preventDefault()\n if (props.modelValue) close()\n else open()\n }\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.body.style.overflow = 'hidden'\n nextTick(() => inputRef.value?.focus())\n } else {\n document.body.style.overflow = ''\n }\n },\n)\n\nwatch(query, () => { activeIndex.value = 0 })\n\nonMounted(() => document.addEventListener('keydown', onGlobalKeydown))\nonBeforeUnmount(() => document.removeEventListener('keydown', onGlobalKeydown))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-cmd\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-black/40 pt-[15vh]\"\n @click.self=\"close\"\n >\n <div class=\"cmd-box flex w-full max-w-lg flex-col overflow-hidden rounded-xl bg-surface-container-high shadow-elevation-3\">\n <!-- Search input -->\n <div class=\"flex items-center gap-3 border-b border-outline-variant px-4\">\n <MIcon name=\"search\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n ref=\"inputRef\"\n v-model=\"query\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"h-12 flex-1 bg-transparent text-body-large text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n @keydown=\"onKeydown\"\n />\n <kbd class=\"rounded bg-surface-container px-1.5 py-0.5 text-label-small text-on-surface-variant\">\n ESC\n </kbd>\n </div>\n\n <!-- Results -->\n <div class=\"max-h-80 overflow-y-auto py-2\">\n <template v-if=\"filtered.length\">\n <template v-for=\"[group, items] in grouped\" :key=\"group\">\n <p v-if=\"group\" class=\"px-4 pt-3 pb-1 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ group }}\n </p>\n <button\n v-for=\"(item, i) in items\"\n :key=\"item.id\"\n type=\"button\"\n :data-cmd-active=\"filtered.indexOf(item) === activeIndex || undefined\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left transition-colors\"\n :class=\"filtered.indexOf(item) === activeIndex ? 'bg-primary/12 text-primary' : 'text-on-surface hover:bg-on-surface/4'\"\n @click=\"selectItem(item)\"\n @pointerenter=\"activeIndex = filtered.indexOf(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"20\" class=\"shrink-0 opacity-70\" />\n <span class=\"flex-1 truncate text-body-medium\">{{ item.label }}</span>\n <kbd v-if=\"item.shortcut\" class=\"rounded bg-surface-container px-1.5 py-0.5 text-label-small text-on-surface-variant\">\n {{ item.shortcut }}\n </kbd>\n </button>\n </template>\n </template>\n <p v-else class=\"px-4 py-6 text-center text-body-medium text-on-surface-variant\">\n {{ noResultsText }}\n </p>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center gap-4 border-t border-outline-variant px-4 py-2\">\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↑↓</kbd> navegar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↵</kbd> seleccionar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">esc</kbd> cerrar\n </span>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-cmd-enter-active,\n.m3-cmd-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-cmd-enter-from,\n.m3-cmd-leave-to {\n opacity: 0;\n}\n.m3-cmd-enter-active .cmd-box,\n.m3-cmd-leave-active .cmd-box {\n transition: transform 0.15s ease;\n}\n.m3-cmd-enter-from .cmd-box,\n.m3-cmd-leave-to .cmd-box {\n transform: scale(0.95) translateY(-10px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n title?: string\n maxWidth?: string\n persistent?: boolean\n }>(),\n {\n maxWidth: 'max-w-md',\n persistent: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\nfunction close() {\n if (props.persistent) return\n emit('update:modelValue', false)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') close()\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.addEventListener('keydown', onKeydown)\n document.body.style.overflow = 'hidden'\n } else {\n document.removeEventListener('keydown', onKeydown)\n document.body.style.overflow = ''\n }\n },\n)\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-dialog\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4\"\n @click.self=\"close\"\n >\n <div\n class=\"dialog-box flex max-h-[90vh] w-full flex-col rounded-xl bg-surface-container-high shadow-elevation-3\"\n :class=\"maxWidth\"\n >\n <div class=\"flex items-start justify-between gap-4 px-6 pt-6 pb-2\">\n <h2 class=\"text-headline-small text-on-surface\">\n <slot name=\"title\">{{ title }}</slot>\n </h2>\n <MIconButton v-if=\"!persistent\" icon=\"close\" label=\"Cerrar\" @click=\"close\" />\n </div>\n <div class=\"overflow-y-auto px-6 py-2 text-body-medium text-on-surface-variant\">\n <slot />\n </div>\n <div v-if=\"$slots.actions\" class=\"flex justify-end gap-2 px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-dialog-enter-active,\n.m3-dialog-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-dialog-enter-from,\n.m3-dialog-leave-to {\n opacity: 0;\n}\n.m3-dialog-enter-active .dialog-box,\n.m3-dialog-leave-active .dialog-box {\n transition: transform 0.15s ease;\n}\n.m3-dialog-enter-from .dialog-box,\n.m3-dialog-leave-to .dialog-box {\n transform: scale(0.95);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n title?: string\n maxWidth?: string\n persistent?: boolean\n }>(),\n {\n maxWidth: 'max-w-md',\n persistent: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\nfunction close() {\n if (props.persistent) return\n emit('update:modelValue', false)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') close()\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.addEventListener('keydown', onKeydown)\n document.body.style.overflow = 'hidden'\n } else {\n document.removeEventListener('keydown', onKeydown)\n document.body.style.overflow = ''\n }\n },\n)\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-dialog\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4\"\n @click.self=\"close\"\n >\n <div\n class=\"dialog-box flex max-h-[90vh] w-full flex-col rounded-xl bg-surface-container-high shadow-elevation-3\"\n :class=\"maxWidth\"\n >\n <div class=\"flex items-start justify-between gap-4 px-6 pt-6 pb-2\">\n <h2 class=\"text-headline-small text-on-surface\">\n <slot name=\"title\">{{ title }}</slot>\n </h2>\n <MIconButton v-if=\"!persistent\" icon=\"close\" label=\"Cerrar\" @click=\"close\" />\n </div>\n <div class=\"overflow-y-auto px-6 py-2 text-body-medium text-on-surface-variant\">\n <slot />\n </div>\n <div v-if=\"$slots.actions\" class=\"flex justify-end gap-2 px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-dialog-enter-active,\n.m3-dialog-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-dialog-enter-from,\n.m3-dialog-leave-to {\n opacity: 0;\n}\n.m3-dialog-enter-active .dialog-box,\n.m3-dialog-leave-active .dialog-box {\n transition: transform 0.15s ease;\n}\n.m3-dialog-enter-from .dialog-box,\n.m3-dialog-leave-to .dialog-box {\n transform: scale(0.95);\n}\n</style>\n","<script setup lang=\"ts\">\nimport MDialog from './MDialog.vue'\nimport MButton from './MButton.vue'\n\nwithDefaults(\n defineProps<{\n modelValue: boolean\n title: string\n message: string\n confirmLabel?: string\n cancelLabel?: string\n danger?: boolean\n loading?: boolean\n }>(),\n {\n confirmLabel: 'Confirmar',\n cancelLabel: 'Cancelar',\n danger: false,\n loading: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean]; confirm: [] }>()\n</script>\n\n<template>\n <MDialog\n :model-value=\"modelValue\"\n :title=\"title\"\n max-width=\"max-w-sm\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <p class=\"text-body-medium text-on-surface-variant\">{{ message }}</p>\n <template #actions>\n <MButton variant=\"text\" :disabled=\"loading\" @click=\"emit('update:modelValue', false)\">\n {{ cancelLabel }}\n </MButton>\n <MButton :color=\"danger ? 'error' : 'primary'\" :loading=\"loading\" @click=\"emit('confirm')\">\n {{ confirmLabel }}\n </MButton>\n </template>\n </MDialog>\n</template>\n","<script setup lang=\"ts\">\nimport MDialog from './MDialog.vue'\nimport MButton from './MButton.vue'\n\nwithDefaults(\n defineProps<{\n modelValue: boolean\n title: string\n message: string\n confirmLabel?: string\n cancelLabel?: string\n danger?: boolean\n loading?: boolean\n }>(),\n {\n confirmLabel: 'Confirmar',\n cancelLabel: 'Cancelar',\n danger: false,\n loading: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean]; confirm: [] }>()\n</script>\n\n<template>\n <MDialog\n :model-value=\"modelValue\"\n :title=\"title\"\n max-width=\"max-w-sm\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <p class=\"text-body-medium text-on-surface-variant\">{{ message }}</p>\n <template #actions>\n <MButton variant=\"text\" :disabled=\"loading\" @click=\"emit('update:modelValue', false)\">\n {{ cancelLabel }}\n </MButton>\n <MButton :color=\"danger ? 'error' : 'primary'\" :loading=\"loading\" @click=\"emit('confirm')\">\n {{ confirmLabel }}\n </MButton>\n </template>\n </MDialog>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\n fluid?: boolean\n centered?: boolean\n padding?: boolean\n }>(),\n { maxWidth: 'lg', fluid: false, centered: true, padding: true },\n)\n\nconst maxWidthClasses: Record<string, string> = {\n xs: 'max-w-screen-xs',\n sm: 'max-w-screen-sm',\n md: 'max-w-screen-md',\n lg: 'max-w-screen-lg',\n xl: 'max-w-screen-xl',\n '2xl': 'max-w-screen-2xl',\n full: 'max-w-full',\n}\n\nconst classes = computed(() => [\n 'w-full',\n props.fluid ? 'max-w-full' : maxWidthClasses[props.maxWidth],\n props.centered && 'mx-auto',\n props.padding && 'px-4 sm:px-6 lg:px-8',\n])\n</script>\n\n<template>\n <div :class=\"classes\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\n fluid?: boolean\n centered?: boolean\n padding?: boolean\n }>(),\n { maxWidth: 'lg', fluid: false, centered: true, padding: true },\n)\n\nconst maxWidthClasses: Record<string, string> = {\n xs: 'max-w-screen-xs',\n sm: 'max-w-screen-sm',\n md: 'max-w-screen-md',\n lg: 'max-w-screen-lg',\n xl: 'max-w-screen-xl',\n '2xl': 'max-w-screen-2xl',\n full: 'max-w-full',\n}\n\nconst classes = computed(() => [\n 'w-full',\n props.fluid ? 'max-w-full' : maxWidthClasses[props.maxWidth],\n props.centered && 'mx-auto',\n props.padding && 'px-4 sm:px-6 lg:px-8',\n])\n</script>\n\n<template>\n <div :class=\"classes\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, nextTick } from \"vue\";\nimport MIcon from \"./MIcon.vue\";\nimport MContextMenuPanel from \"./_MContextMenuPanel.vue\";\nimport type { ContextMenuItem } from \"./MContextMenu.vue\";\n\nconst props = defineProps<{\n items: ContextMenuItem[];\n x: number;\n y: number;\n}>();\n\nconst emit = defineEmits<{ close: [] }>();\n\nconst panel = ref<HTMLElement | null>(null);\nconst panelX = ref(props.x);\nconst panelY = ref(props.y);\nconst activeIndex = ref<number | null>(null);\nconst subPos = ref({ x: 0, y: 0 });\n\nonMounted(async () => {\n await nextTick();\n if (!panel.value) return;\n const el = panel.value;\n panelX.value = Math.min(props.x, window.innerWidth - el.offsetWidth - 8);\n panelY.value = Math.min(props.y, window.innerHeight - el.offsetHeight - 8);\n});\n\nfunction onItemMouseEnter(index: number, item: ContextMenuItem, e: MouseEvent) {\n if (item.divider || item.disabled) {\n activeIndex.value = null;\n return;\n }\n if (!item.children?.length) {\n activeIndex.value = null;\n return;\n }\n\n activeIndex.value = index;\n const itemEl = e.currentTarget as HTMLElement;\n const itemRect = itemEl.getBoundingClientRect();\n const panelRect = panel.value!.getBoundingClientRect();\n\n let x = panelRect.right;\n let y = itemRect.top;\n if (x + 220 > window.innerWidth) x = panelRect.left - 220;\n if (y + 300 > window.innerHeight) y = Math.max(8, window.innerHeight - 300);\n\n subPos.value = { x, y };\n}\n\nfunction onItemClick(item: ContextMenuItem) {\n if (item.disabled || item.divider || item.children?.length) return;\n item.onClick?.();\n emit(\"close\");\n}\n\nfunction onPanelMouseLeave(e: MouseEvent) {\n // Don't close if the mouse moved to another context menu panel (sibling sub-panel)\n const related = e.relatedTarget as Element | null;\n if (related?.closest(\".m3-ctx-panel\")) return;\n activeIndex.value = null;\n}\n</script>\n\n<template>\n <div\n ref=\"panel\"\n class=\"m3-ctx-panel absolute z-[201] min-w-[200px] rounded-sm bg-surface-container shadow-elevation-2\"\n :style=\"{ left: `${panelX}px`, top: `${panelY}px` }\"\n @mouseleave=\"onPanelMouseLeave\"\n >\n <div class=\"overflow-hidden rounded-sm py-1\">\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <hr v-if=\"item.divider\" class=\"my-1 border-outline-variant\" />\n\n <div\n v-else\n class=\"relative flex cursor-default select-none items-center gap-3 px-4 py-2.5 text-body-large\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-38 text-on-surface'\n : item.danger\n ? 'cursor-pointer text-error hover:bg-error/8'\n : 'cursor-pointer text-on-surface hover:bg-on-surface/8',\n activeIndex === i && !item.disabled\n ? item.danger\n ? 'bg-error/8'\n : 'bg-on-surface/8'\n : '',\n ]\"\n @mouseenter=\"onItemMouseEnter(i, item, $event)\"\n @click=\"onItemClick(item)\"\n >\n <MIcon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n :size=\"18\"\n class=\"shrink-0\"\n :class=\"item.danger ? 'text-error' : 'text-on-surface-variant'\"\n />\n <span v-else class=\"w-[18px] shrink-0\" />\n\n <span class=\"flex-1\">{{ item.label }}</span>\n\n <span\n v-if=\"item.shortcut\"\n class=\"text-label-small text-on-surface-variant\"\n >\n {{ item.shortcut }}\n </span>\n\n <MIcon\n v-if=\"item.children?.length\"\n name=\"chevron_right\"\n :size=\"18\"\n class=\"shrink-0 text-on-surface-variant\"\n />\n </div>\n </template>\n </div>\n </div>\n\n <!-- Sub-panel: sibling in the same Teleport layer.\n No <Transition> wrapper — MContextMenuPanel is a fragment and cannot be\n animated by Vue's Transition (produces a console warning). -->\n <MContextMenuPanel\n v-if=\"activeIndex !== null && items[activeIndex]?.children?.length\"\n :key=\"activeIndex\"\n :items=\"items[activeIndex]!.children!\"\n :x=\"subPos.x\"\n :y=\"subPos.y\"\n @close=\"emit('close')\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, nextTick } from \"vue\";\nimport MIcon from \"./MIcon.vue\";\nimport MContextMenuPanel from \"./_MContextMenuPanel.vue\";\nimport type { ContextMenuItem } from \"./MContextMenu.vue\";\n\nconst props = defineProps<{\n items: ContextMenuItem[];\n x: number;\n y: number;\n}>();\n\nconst emit = defineEmits<{ close: [] }>();\n\nconst panel = ref<HTMLElement | null>(null);\nconst panelX = ref(props.x);\nconst panelY = ref(props.y);\nconst activeIndex = ref<number | null>(null);\nconst subPos = ref({ x: 0, y: 0 });\n\nonMounted(async () => {\n await nextTick();\n if (!panel.value) return;\n const el = panel.value;\n panelX.value = Math.min(props.x, window.innerWidth - el.offsetWidth - 8);\n panelY.value = Math.min(props.y, window.innerHeight - el.offsetHeight - 8);\n});\n\nfunction onItemMouseEnter(index: number, item: ContextMenuItem, e: MouseEvent) {\n if (item.divider || item.disabled) {\n activeIndex.value = null;\n return;\n }\n if (!item.children?.length) {\n activeIndex.value = null;\n return;\n }\n\n activeIndex.value = index;\n const itemEl = e.currentTarget as HTMLElement;\n const itemRect = itemEl.getBoundingClientRect();\n const panelRect = panel.value!.getBoundingClientRect();\n\n let x = panelRect.right;\n let y = itemRect.top;\n if (x + 220 > window.innerWidth) x = panelRect.left - 220;\n if (y + 300 > window.innerHeight) y = Math.max(8, window.innerHeight - 300);\n\n subPos.value = { x, y };\n}\n\nfunction onItemClick(item: ContextMenuItem) {\n if (item.disabled || item.divider || item.children?.length) return;\n item.onClick?.();\n emit(\"close\");\n}\n\nfunction onPanelMouseLeave(e: MouseEvent) {\n // Don't close if the mouse moved to another context menu panel (sibling sub-panel)\n const related = e.relatedTarget as Element | null;\n if (related?.closest(\".m3-ctx-panel\")) return;\n activeIndex.value = null;\n}\n</script>\n\n<template>\n <div\n ref=\"panel\"\n class=\"m3-ctx-panel absolute z-[201] min-w-[200px] rounded-sm bg-surface-container shadow-elevation-2\"\n :style=\"{ left: `${panelX}px`, top: `${panelY}px` }\"\n @mouseleave=\"onPanelMouseLeave\"\n >\n <div class=\"overflow-hidden rounded-sm py-1\">\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <hr v-if=\"item.divider\" class=\"my-1 border-outline-variant\" />\n\n <div\n v-else\n class=\"relative flex cursor-default select-none items-center gap-3 px-4 py-2.5 text-body-large\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-38 text-on-surface'\n : item.danger\n ? 'cursor-pointer text-error hover:bg-error/8'\n : 'cursor-pointer text-on-surface hover:bg-on-surface/8',\n activeIndex === i && !item.disabled\n ? item.danger\n ? 'bg-error/8'\n : 'bg-on-surface/8'\n : '',\n ]\"\n @mouseenter=\"onItemMouseEnter(i, item, $event)\"\n @click=\"onItemClick(item)\"\n >\n <MIcon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n :size=\"18\"\n class=\"shrink-0\"\n :class=\"item.danger ? 'text-error' : 'text-on-surface-variant'\"\n />\n <span v-else class=\"w-[18px] shrink-0\" />\n\n <span class=\"flex-1\">{{ item.label }}</span>\n\n <span\n v-if=\"item.shortcut\"\n class=\"text-label-small text-on-surface-variant\"\n >\n {{ item.shortcut }}\n </span>\n\n <MIcon\n v-if=\"item.children?.length\"\n name=\"chevron_right\"\n :size=\"18\"\n class=\"shrink-0 text-on-surface-variant\"\n />\n </div>\n </template>\n </div>\n </div>\n\n <!-- Sub-panel: sibling in the same Teleport layer.\n No <Transition> wrapper — MContextMenuPanel is a fragment and cannot be\n animated by Vue's Transition (produces a console warning). -->\n <MContextMenuPanel\n v-if=\"activeIndex !== null && items[activeIndex]?.children?.length\"\n :key=\"activeIndex\"\n :items=\"items[activeIndex]!.children!\"\n :x=\"subPos.x\"\n :y=\"subPos.y\"\n @close=\"emit('close')\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport MContextMenuPanel from './_MContextMenuPanel.vue'\n\nexport interface ContextMenuItem {\n label?: string\n icon?: string\n shortcut?: string\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n children?: ContextMenuItem[]\n onClick?: () => void\n}\n\ndefineProps<{ items: ContextMenuItem[] }>()\n\nconst visible = ref(false)\nconst position = ref({ x: 0, y: 0 })\n\nfunction show(e: MouseEvent) {\n e.preventDefault()\n e.stopPropagation()\n showAt(e.clientX, e.clientY)\n}\n\nfunction showAt(x: number, y: number) {\n position.value = { x, y }\n visible.value = true\n}\n\nfunction hide() {\n visible.value = false\n}\n\ndefineExpose({ show, showAt, hide })\n</script>\n\n<template>\n <slot :show=\"show\" />\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-100\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"fixed inset-0 z-[200]\"\n @mousedown.self=\"hide\"\n @contextmenu.prevent\n >\n <MContextMenuPanel\n :items=\"items\"\n :x=\"position.x\"\n :y=\"position.y\"\n @close=\"hide\"\n />\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport MContextMenuPanel from './_MContextMenuPanel.vue'\n\nexport interface ContextMenuItem {\n label?: string\n icon?: string\n shortcut?: string\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n children?: ContextMenuItem[]\n onClick?: () => void\n}\n\ndefineProps<{ items: ContextMenuItem[] }>()\n\nconst visible = ref(false)\nconst position = ref({ x: 0, y: 0 })\n\nfunction show(e: MouseEvent) {\n e.preventDefault()\n e.stopPropagation()\n showAt(e.clientX, e.clientY)\n}\n\nfunction showAt(x: number, y: number) {\n position.value = { x, y }\n visible.value = true\n}\n\nfunction hide() {\n visible.value = false\n}\n\ndefineExpose({ show, showAt, hide })\n</script>\n\n<template>\n <slot :show=\"show\" />\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-100\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"fixed inset-0 z-[200]\"\n @mousedown.self=\"hide\"\n @contextmenu.prevent\n >\n <MContextMenuPanel\n :items=\"items\"\n :x=\"position.x\"\n :y=\"position.y\"\n @close=\"hide\"\n />\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nconst props = defineProps<{ page: number; perPage: number; total: number }>()\nconst emit = defineEmits<{ 'update:page': [number] }>()\n\nconst totalPages = computed(() => Math.max(1, Math.ceil(props.total / props.perPage)))\n\nconst rangeLabel = computed(() => {\n if (props.total === 0) return '0 resultados'\n const from = (props.page - 1) * props.perPage + 1\n const to = Math.min(props.page * props.perPage, props.total)\n return `${from}-${to} de ${props.total}`\n})\n</script>\n\n<template>\n <div class=\"flex flex-wrap items-center justify-between gap-4 text-body-medium text-on-surface-variant\">\n <span>{{ rangeLabel }}</span>\n <div class=\"flex items-center gap-2\">\n <span>Página {{ page }} de {{ totalPages }}</span>\n <MIconButton\n icon=\"chevron_left\"\n label=\"Página anterior\"\n :disabled=\"page <= 1\"\n @click=\"emit('update:page', page - 1)\"\n />\n <MIconButton\n icon=\"chevron_right\"\n label=\"Página siguiente\"\n :disabled=\"page >= totalPages\"\n @click=\"emit('update:page', page + 1)\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nconst props = defineProps<{ page: number; perPage: number; total: number }>()\nconst emit = defineEmits<{ 'update:page': [number] }>()\n\nconst totalPages = computed(() => Math.max(1, Math.ceil(props.total / props.perPage)))\n\nconst rangeLabel = computed(() => {\n if (props.total === 0) return '0 resultados'\n const from = (props.page - 1) * props.perPage + 1\n const to = Math.min(props.page * props.perPage, props.total)\n return `${from}-${to} de ${props.total}`\n})\n</script>\n\n<template>\n <div class=\"flex flex-wrap items-center justify-between gap-4 text-body-medium text-on-surface-variant\">\n <span>{{ rangeLabel }}</span>\n <div class=\"flex items-center gap-2\">\n <span>Página {{ page }} de {{ totalPages }}</span>\n <MIconButton\n icon=\"chevron_left\"\n label=\"Página anterior\"\n :disabled=\"page <= 1\"\n @click=\"emit('update:page', page - 1)\"\n />\n <MIconButton\n icon=\"chevron_right\"\n label=\"Página siguiente\"\n :disabled=\"page >= totalPages\"\n @click=\"emit('update:page', page + 1)\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useSlots, watch } from 'vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MPagination from './MPagination.vue'\nimport MChip from './MChip.vue'\n\nexport interface DataTableColumn {\n key: string\n label: string\n sortable?: boolean\n filterable?: boolean\n resizable?: boolean\n width?: string\n minWidth?: string\n align?: 'left' | 'center' | 'right'\n pinned?: 'left' | 'right'\n hidden?: boolean\n}\n\nexport interface DataTableGroup {\n key: string\n label: string\n}\n\nconst SKEL = [65, 80, 50, 75, 90, 55, 70, 85, 60, 78]\n\nconst props = withDefaults(defineProps<{\n columns: DataTableColumn[]\n rows: Record<string, any>[]\n loading?: boolean\n emptyText?: string\n rowKey?: string\n selectable?: boolean\n modelValue?: Record<string, any>[]\n perPage?: number\n searchable?: boolean\n expandable?: boolean\n striped?: boolean\n dense?: boolean\n stickyHeader?: boolean\n groupBy?: string\n columnToggle?: boolean\n exportable?: boolean\n}>(), {\n loading: false,\n emptyText: 'Sin resultados',\n rowKey: 'id',\n selectable: false,\n modelValue: () => [],\n perPage: 10,\n searchable: true,\n expandable: false,\n striped: false,\n dense: false,\n stickyHeader: false,\n columnToggle: false,\n exportable: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [Record<string, any>[]]\n rowClick: [Record<string, any>]\n}>()\n\nconst slots = useSlots()\nconst hasActions = computed(() => !!slots['row-actions'])\nconst hasExpand = computed(() => props.expandable && !!slots['row-expand'])\n\nconst search = ref('')\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\nconst internalPage = ref(1)\nconst expanded = ref<Set<any>>(new Set())\nconst hiddenCols = ref<Set<string>>(new Set())\nconst colWidths = ref<Record<string, number>>({})\nconst showColMenu = ref(false)\n\nconst visibleColumns = computed(() =>\n props.columns.filter(c => !c.hidden && !hiddenCols.value.has(c.key))\n)\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) { sortKey.value = key; sortDir.value = 'asc' }\n else if (sortDir.value === 'asc') sortDir.value = 'desc'\n else { sortKey.value = ''; sortDir.value = '' }\n}\n\nconst processedRows = computed(() => {\n let result = props.rows\n if (search.value.trim()) {\n const q = search.value.toLowerCase()\n result = result.filter(row =>\n visibleColumns.value.some(col => {\n const val = row[col.key]\n return val != null && String(val).toLowerCase().includes(q)\n })\n )\n }\n if (sortKey.value && sortDir.value) {\n const key = sortKey.value, dir = sortDir.value\n result = [...result].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, { numeric: true, sensitivity: 'base' })\n return dir === 'asc' ? cmp : -cmp\n })\n }\n return result\n})\n\nconst groupedRows = computed(() => {\n if (!props.groupBy) return null\n const map = new Map<string, Record<string, any>[]>()\n for (const row of processedRows.value) {\n const key = String(row[props.groupBy] ?? 'Sin grupo')\n if (!map.has(key)) map.set(key, [])\n map.get(key)!.push(row)\n }\n return map\n})\n\nconst totalCount = computed(() => processedRows.value.length)\nconst visibleRows = computed(() => {\n const start = (internalPage.value - 1) * props.perPage\n return processedRows.value.slice(start, start + props.perPage)\n})\n\nwatch([search, sortKey, sortDir], () => { internalPage.value = 1 })\n\nconst selected = computed({\n get: () => props.modelValue ?? [],\n set: (val) => emit('update:modelValue', val),\n})\nfunction rowId(row: Record<string, any>) { return row[props.rowKey] }\nfunction isSelected(row: Record<string, any>) { return selected.value.some(r => rowId(r) === rowId(row)) }\nfunction toggleRow(row: Record<string, any>) {\n if (isSelected(row)) selected.value = selected.value.filter(r => rowId(r) !== rowId(row))\n else selected.value = [...selected.value, row]\n}\nconst allOnPageSelected = computed(() => visibleRows.value.length > 0 && visibleRows.value.every(r => isSelected(r)))\nconst someOnPageSelected = computed(() => visibleRows.value.some(r => isSelected(r)) && !allOnPageSelected.value)\nfunction toggleAll() {\n if (allOnPageSelected.value) selected.value = selected.value.filter(r => !visibleRows.value.some(v => rowId(v) === rowId(r)))\n else selected.value = [...selected.value, ...visibleRows.value.filter(r => !isSelected(r))]\n}\n\nfunction toggleExpand(row: Record<string, any>) {\n const id = rowId(row)\n const next = new Set(expanded.value)\n next.has(id) ? next.delete(id) : next.add(id)\n expanded.value = next\n}\nfunction isExpanded(row: Record<string, any>) { return expanded.value.has(rowId(row)) }\n\nconst extraCols = computed(() =>\n (props.selectable ? 1 : 0) + (hasActions.value ? 1 : 0) + (hasExpand.value ? 1 : 0)\n)\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\nfunction skelWidth(ri: number, ci: number) { return `${SKEL[(ri * 3 + ci) % SKEL.length]}%` }\n\nlet resizeCol: string | null = null\nlet resizeStart = 0\nlet resizeInitial = 0\n\nfunction onResizeDown(e: PointerEvent, col: DataTableColumn) {\n e.preventDefault()\n resizeCol = col.key\n resizeStart = e.clientX\n resizeInitial = colWidths.value[col.key] ?? 150\n window.addEventListener('pointermove', onResizeMove)\n window.addEventListener('pointerup', onResizeUp)\n}\nfunction onResizeMove(e: PointerEvent) {\n if (!resizeCol) return\n const w = Math.max(60, resizeInitial + e.clientX - resizeStart)\n colWidths.value = { ...colWidths.value, [resizeCol]: w }\n}\nfunction onResizeUp() {\n resizeCol = null\n window.removeEventListener('pointermove', onResizeMove)\n window.removeEventListener('pointerup', onResizeUp)\n}\n\nfunction exportCSV() {\n const cols = visibleColumns.value\n const header = cols.map(c => c.label).join(',')\n const body = processedRows.value.map(row =>\n cols.map(c => {\n const v = String(row[c.key] ?? '')\n return v.includes(',') || v.includes('\"') ? `\"${v.replace(/\"/g, '\"\"')}\"` : v\n }).join(',')\n ).join('\\n')\n const blob = new Blob([`${header}\\n${body}`], { type: 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url; a.download = 'data.csv'; a.click()\n URL.revokeObjectURL(url)\n}\n\nfunction colStyle(col: DataTableColumn) {\n const w = colWidths.value[col.key]\n if (w) return { width: `${w}px`, minWidth: col.minWidth }\n return { width: col.width, minWidth: col.minWidth }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n\n <!-- Toolbar -->\n <div\n v-if=\"searchable || columnToggle || exportable || $slots.toolbar\"\n class=\"flex flex-wrap items-center gap-3 border-b border-outline-variant bg-surface-container-lowest px-4 py-2.5\"\n >\n <div v-if=\"searchable\" class=\"flex min-w-48 flex-1 items-center gap-2 rounded-full border border-outline-variant bg-surface-container px-3 py-1.5 transition-[border-color,box-shadow] duration-150 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input v-model=\"search\" type=\"text\" placeholder=\"Buscar...\" class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\" />\n <button v-if=\"search\" class=\"text-on-surface-variant transition-colors hover:text-on-surface\" @click=\"search = ''\">\n <MIcon name=\"close\" :size=\"14\" />\n </button>\n </div>\n\n <slot name=\"toolbar\" />\n\n <Transition enter-active-class=\"transition-[opacity,transform] duration-150\" enter-from-class=\"opacity-0 scale-90\" leave-active-class=\"transition-[opacity,transform] duration-100\" leave-to-class=\"opacity-0 scale-90\">\n <span v-if=\"selectable && selected.length > 0\" class=\"rounded-full bg-primary/12 px-3 py-1 text-label-small font-medium text-primary\">\n {{ selected.length }} seleccionado{{ selected.length !== 1 ? 's' : '' }}\n </span>\n </Transition>\n\n <!-- Column toggle -->\n <div v-if=\"columnToggle\" class=\"relative\">\n <MIconButton icon=\"view_column\" label=\"Columnas\" :size=\"36\" @click=\"showColMenu = !showColMenu\" />\n <div v-if=\"showColMenu\" class=\"absolute right-0 top-full z-10 mt-1 min-w-40 rounded-lg bg-surface-container py-2 shadow-elevation-3\">\n <label v-for=\"col in columns\" :key=\"col.key\" class=\"flex cursor-pointer items-center gap-2 px-3 py-1.5 hover:bg-on-surface/4\">\n <MCheckbox\n :model-value=\"!hiddenCols.has(col.key)\"\n @update:model-value=\"hiddenCols.has(col.key) ? hiddenCols.delete(col.key) : hiddenCols.add(col.key)\"\n />\n <span class=\"text-body-small text-on-surface\">{{ col.label }}</span>\n </label>\n </div>\n </div>\n\n <MIconButton v-if=\"exportable\" icon=\"download\" label=\"Exportar CSV\" :size=\"36\" @click=\"exportCSV\" />\n </div>\n\n <!-- Table -->\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead :class=\"stickyHeader ? 'sticky top-0 z-[1]' : ''\">\n <tr class=\"bg-surface-container-high\">\n <th v-if=\"hasExpand\" class=\"w-10 px-2\" :class=\"dense ? 'py-2' : 'py-3'\" />\n <th v-if=\"selectable\" class=\"w-12 px-4\" :class=\"dense ? 'py-2' : 'py-3'\">\n <MCheckbox :model-value=\"allOnPageSelected\" :indeterminate=\"someOnPageSelected\" @update:model-value=\"toggleAll\" />\n </th>\n <th\n v-for=\"col in visibleColumns\"\n :key=\"col.key\"\n :style=\"colStyle(col)\"\n :class=\"[\n 'relative whitespace-nowrap text-label-medium font-medium text-on-surface-variant',\n dense ? 'px-3 py-2' : 'px-4 py-3',\n alignClass(col.align),\n col.sortable ? 'cursor-pointer select-none hover:text-on-surface transition-colors duration-100' : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon v-if=\"sortKey === col.key && sortDir === 'asc'\" name=\"arrow_upward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else-if=\"sortKey === col.key && sortDir === 'desc'\" name=\"arrow_downward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n <!-- Resize handle -->\n <div\n v-if=\"col.resizable\"\n class=\"absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-primary/30\"\n @pointerdown=\"onResizeDown($event, col)\"\n />\n </th>\n <th v-if=\"hasActions\" class=\"w-1 px-4\" :class=\"dense ? 'py-2' : 'py-3'\" />\n </tr>\n </thead>\n\n <tbody>\n <!-- Loading -->\n <template v-if=\"loading\">\n <tr v-for=\"ri in perPage\" :key=\"`sk-${ri}`\" class=\"border-t border-outline-variant\">\n <td v-if=\"hasExpand\" :class=\"dense ? 'px-2 py-2' : 'px-2 py-3'\" />\n <td v-if=\"selectable\" :class=\"dense ? 'px-4 py-2' : 'px-4 py-3.5'\">\n <div class=\"h-4 w-4 animate-pulse rounded bg-on-surface/10\" />\n </td>\n <td v-for=\"(col, ci) in visibleColumns\" :key=\"col.key\" :class=\"dense ? 'px-3 py-2' : 'px-4 py-3.5'\">\n <div class=\"h-4 animate-pulse rounded-full bg-on-surface/10\" :style=\"{ width: skelWidth(ri, ci) }\" />\n </td>\n <td v-if=\"hasActions\" :class=\"dense ? 'px-4 py-2' : 'px-4 py-3.5'\">\n <div class=\"ml-auto h-4 w-16 animate-pulse rounded-full bg-on-surface/10\" />\n </td>\n </tr>\n </template>\n\n <!-- Empty -->\n <template v-else-if=\"visibleRows.length === 0\">\n <tr>\n <td :colspan=\"visibleColumns.length + extraCols\" class=\"border-t border-outline-variant px-4 py-14 text-center\">\n <slot name=\"empty\">\n <MIcon name=\"search_off\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ emptyText }}</p>\n </slot>\n </td>\n </tr>\n </template>\n\n <!-- Data rows -->\n <template v-else>\n <template v-for=\"row in visibleRows\" :key=\"rowId(row)\">\n <tr\n :class=\"[\n 'border-t border-outline-variant transition-colors duration-100',\n 'hover:bg-on-surface/[0.04]',\n selectable && isSelected(row) ? 'bg-primary/[0.06]' : '',\n striped ? 'even:bg-surface-container-lowest' : '',\n selectable ? 'cursor-pointer' : '',\n ]\"\n @click=\"selectable ? toggleRow(row) : emit('rowClick', row)\"\n >\n <td v-if=\"hasExpand\" class=\"px-2\" :class=\"dense ? 'py-1' : 'py-2'\" @click.stop>\n <MIconButton\n icon=\"expand_more\"\n label=\"Expandir\"\n :size=\"28\"\n :class=\"isExpanded(row) ? 'rotate-180' : ''\"\n class=\"transition-transform duration-200\"\n @click=\"toggleExpand(row)\"\n />\n </td>\n <td v-if=\"selectable\" :class=\"dense ? 'px-4 py-1' : 'px-4 py-3'\" @click.stop=\"toggleRow(row)\">\n <MCheckbox :model-value=\"isSelected(row)\" @update:model-value=\"toggleRow(row)\" />\n </td>\n <td\n v-for=\"col in visibleColumns\"\n :key=\"col.key\"\n :class=\"['text-body-medium text-on-surface', alignClass(col.align), dense ? 'px-3 py-1.5' : 'px-4 py-3']\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\" :col=\"col\">\n {{ row[col.key] ?? '—' }}\n </slot>\n </td>\n <td v-if=\"hasActions\" class=\"text-right\" :class=\"dense ? 'px-4 py-1' : 'px-4 py-3'\" @click.stop>\n <slot name=\"row-actions\" :row=\"row\" />\n </td>\n </tr>\n <!-- Expanded content -->\n <tr v-if=\"hasExpand && isExpanded(row)\">\n <td :colspan=\"visibleColumns.length + extraCols\" class=\"border-t border-outline-variant/50 bg-surface-container-lowest px-6 py-4\">\n <slot name=\"row-expand\" :row=\"row\" />\n </td>\n </tr>\n </template>\n </template>\n </tbody>\n </table>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center justify-between gap-4 border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ totalCount }} registro{{ totalCount !== 1 ? 's' : '' }}\n </span>\n <MPagination :page=\"internalPage\" :per-page=\"perPage\" :total=\"totalCount\" @update:page=\"internalPage = $event\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useSlots, watch } from 'vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MPagination from './MPagination.vue'\nimport MChip from './MChip.vue'\n\nexport interface DataTableColumn {\n key: string\n label: string\n sortable?: boolean\n filterable?: boolean\n resizable?: boolean\n width?: string\n minWidth?: string\n align?: 'left' | 'center' | 'right'\n pinned?: 'left' | 'right'\n hidden?: boolean\n}\n\nexport interface DataTableGroup {\n key: string\n label: string\n}\n\nconst SKEL = [65, 80, 50, 75, 90, 55, 70, 85, 60, 78]\n\nconst props = withDefaults(defineProps<{\n columns: DataTableColumn[]\n rows: Record<string, any>[]\n loading?: boolean\n emptyText?: string\n rowKey?: string\n selectable?: boolean\n modelValue?: Record<string, any>[]\n perPage?: number\n searchable?: boolean\n expandable?: boolean\n striped?: boolean\n dense?: boolean\n stickyHeader?: boolean\n groupBy?: string\n columnToggle?: boolean\n exportable?: boolean\n}>(), {\n loading: false,\n emptyText: 'Sin resultados',\n rowKey: 'id',\n selectable: false,\n modelValue: () => [],\n perPage: 10,\n searchable: true,\n expandable: false,\n striped: false,\n dense: false,\n stickyHeader: false,\n columnToggle: false,\n exportable: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [Record<string, any>[]]\n rowClick: [Record<string, any>]\n}>()\n\nconst slots = useSlots()\nconst hasActions = computed(() => !!slots['row-actions'])\nconst hasExpand = computed(() => props.expandable && !!slots['row-expand'])\n\nconst search = ref('')\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\nconst internalPage = ref(1)\nconst expanded = ref<Set<any>>(new Set())\nconst hiddenCols = ref<Set<string>>(new Set())\nconst colWidths = ref<Record<string, number>>({})\nconst showColMenu = ref(false)\n\nconst visibleColumns = computed(() =>\n props.columns.filter(c => !c.hidden && !hiddenCols.value.has(c.key))\n)\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) { sortKey.value = key; sortDir.value = 'asc' }\n else if (sortDir.value === 'asc') sortDir.value = 'desc'\n else { sortKey.value = ''; sortDir.value = '' }\n}\n\nconst processedRows = computed(() => {\n let result = props.rows\n if (search.value.trim()) {\n const q = search.value.toLowerCase()\n result = result.filter(row =>\n visibleColumns.value.some(col => {\n const val = row[col.key]\n return val != null && String(val).toLowerCase().includes(q)\n })\n )\n }\n if (sortKey.value && sortDir.value) {\n const key = sortKey.value, dir = sortDir.value\n result = [...result].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, { numeric: true, sensitivity: 'base' })\n return dir === 'asc' ? cmp : -cmp\n })\n }\n return result\n})\n\nconst groupedRows = computed(() => {\n if (!props.groupBy) return null\n const map = new Map<string, Record<string, any>[]>()\n for (const row of processedRows.value) {\n const key = String(row[props.groupBy] ?? 'Sin grupo')\n if (!map.has(key)) map.set(key, [])\n map.get(key)!.push(row)\n }\n return map\n})\n\nconst totalCount = computed(() => processedRows.value.length)\nconst visibleRows = computed(() => {\n const start = (internalPage.value - 1) * props.perPage\n return processedRows.value.slice(start, start + props.perPage)\n})\n\nwatch([search, sortKey, sortDir], () => { internalPage.value = 1 })\n\nconst selected = computed({\n get: () => props.modelValue ?? [],\n set: (val) => emit('update:modelValue', val),\n})\nfunction rowId(row: Record<string, any>) { return row[props.rowKey] }\nfunction isSelected(row: Record<string, any>) { return selected.value.some(r => rowId(r) === rowId(row)) }\nfunction toggleRow(row: Record<string, any>) {\n if (isSelected(row)) selected.value = selected.value.filter(r => rowId(r) !== rowId(row))\n else selected.value = [...selected.value, row]\n}\nconst allOnPageSelected = computed(() => visibleRows.value.length > 0 && visibleRows.value.every(r => isSelected(r)))\nconst someOnPageSelected = computed(() => visibleRows.value.some(r => isSelected(r)) && !allOnPageSelected.value)\nfunction toggleAll() {\n if (allOnPageSelected.value) selected.value = selected.value.filter(r => !visibleRows.value.some(v => rowId(v) === rowId(r)))\n else selected.value = [...selected.value, ...visibleRows.value.filter(r => !isSelected(r))]\n}\n\nfunction toggleExpand(row: Record<string, any>) {\n const id = rowId(row)\n const next = new Set(expanded.value)\n next.has(id) ? next.delete(id) : next.add(id)\n expanded.value = next\n}\nfunction isExpanded(row: Record<string, any>) { return expanded.value.has(rowId(row)) }\n\nconst extraCols = computed(() =>\n (props.selectable ? 1 : 0) + (hasActions.value ? 1 : 0) + (hasExpand.value ? 1 : 0)\n)\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\nfunction skelWidth(ri: number, ci: number) { return `${SKEL[(ri * 3 + ci) % SKEL.length]}%` }\n\nlet resizeCol: string | null = null\nlet resizeStart = 0\nlet resizeInitial = 0\n\nfunction onResizeDown(e: PointerEvent, col: DataTableColumn) {\n e.preventDefault()\n resizeCol = col.key\n resizeStart = e.clientX\n resizeInitial = colWidths.value[col.key] ?? 150\n window.addEventListener('pointermove', onResizeMove)\n window.addEventListener('pointerup', onResizeUp)\n}\nfunction onResizeMove(e: PointerEvent) {\n if (!resizeCol) return\n const w = Math.max(60, resizeInitial + e.clientX - resizeStart)\n colWidths.value = { ...colWidths.value, [resizeCol]: w }\n}\nfunction onResizeUp() {\n resizeCol = null\n window.removeEventListener('pointermove', onResizeMove)\n window.removeEventListener('pointerup', onResizeUp)\n}\n\nfunction exportCSV() {\n const cols = visibleColumns.value\n const header = cols.map(c => c.label).join(',')\n const body = processedRows.value.map(row =>\n cols.map(c => {\n const v = String(row[c.key] ?? '')\n return v.includes(',') || v.includes('\"') ? `\"${v.replace(/\"/g, '\"\"')}\"` : v\n }).join(',')\n ).join('\\n')\n const blob = new Blob([`${header}\\n${body}`], { type: 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url; a.download = 'data.csv'; a.click()\n URL.revokeObjectURL(url)\n}\n\nfunction colStyle(col: DataTableColumn) {\n const w = colWidths.value[col.key]\n if (w) return { width: `${w}px`, minWidth: col.minWidth }\n return { width: col.width, minWidth: col.minWidth }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n\n <!-- Toolbar -->\n <div\n v-if=\"searchable || columnToggle || exportable || $slots.toolbar\"\n class=\"flex flex-wrap items-center gap-3 border-b border-outline-variant bg-surface-container-lowest px-4 py-2.5\"\n >\n <div v-if=\"searchable\" class=\"flex min-w-48 flex-1 items-center gap-2 rounded-full border border-outline-variant bg-surface-container px-3 py-1.5 transition-[border-color,box-shadow] duration-150 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input v-model=\"search\" type=\"text\" placeholder=\"Buscar...\" class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\" />\n <button v-if=\"search\" class=\"text-on-surface-variant transition-colors hover:text-on-surface\" @click=\"search = ''\">\n <MIcon name=\"close\" :size=\"14\" />\n </button>\n </div>\n\n <slot name=\"toolbar\" />\n\n <Transition enter-active-class=\"transition-[opacity,transform] duration-150\" enter-from-class=\"opacity-0 scale-90\" leave-active-class=\"transition-[opacity,transform] duration-100\" leave-to-class=\"opacity-0 scale-90\">\n <span v-if=\"selectable && selected.length > 0\" class=\"rounded-full bg-primary/12 px-3 py-1 text-label-small font-medium text-primary\">\n {{ selected.length }} seleccionado{{ selected.length !== 1 ? 's' : '' }}\n </span>\n </Transition>\n\n <!-- Column toggle -->\n <div v-if=\"columnToggle\" class=\"relative\">\n <MIconButton icon=\"view_column\" label=\"Columnas\" :size=\"36\" @click=\"showColMenu = !showColMenu\" />\n <div v-if=\"showColMenu\" class=\"absolute right-0 top-full z-10 mt-1 min-w-40 rounded-lg bg-surface-container py-2 shadow-elevation-3\">\n <label v-for=\"col in columns\" :key=\"col.key\" class=\"flex cursor-pointer items-center gap-2 px-3 py-1.5 hover:bg-on-surface/4\">\n <MCheckbox\n :model-value=\"!hiddenCols.has(col.key)\"\n @update:model-value=\"hiddenCols.has(col.key) ? hiddenCols.delete(col.key) : hiddenCols.add(col.key)\"\n />\n <span class=\"text-body-small text-on-surface\">{{ col.label }}</span>\n </label>\n </div>\n </div>\n\n <MIconButton v-if=\"exportable\" icon=\"download\" label=\"Exportar CSV\" :size=\"36\" @click=\"exportCSV\" />\n </div>\n\n <!-- Table -->\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead :class=\"stickyHeader ? 'sticky top-0 z-[1]' : ''\">\n <tr class=\"bg-surface-container-high\">\n <th v-if=\"hasExpand\" class=\"w-10 px-2\" :class=\"dense ? 'py-2' : 'py-3'\" />\n <th v-if=\"selectable\" class=\"w-12 px-4\" :class=\"dense ? 'py-2' : 'py-3'\">\n <MCheckbox :model-value=\"allOnPageSelected\" :indeterminate=\"someOnPageSelected\" @update:model-value=\"toggleAll\" />\n </th>\n <th\n v-for=\"col in visibleColumns\"\n :key=\"col.key\"\n :style=\"colStyle(col)\"\n :class=\"[\n 'relative whitespace-nowrap text-label-medium font-medium text-on-surface-variant',\n dense ? 'px-3 py-2' : 'px-4 py-3',\n alignClass(col.align),\n col.sortable ? 'cursor-pointer select-none hover:text-on-surface transition-colors duration-100' : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon v-if=\"sortKey === col.key && sortDir === 'asc'\" name=\"arrow_upward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else-if=\"sortKey === col.key && sortDir === 'desc'\" name=\"arrow_downward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n <!-- Resize handle -->\n <div\n v-if=\"col.resizable\"\n class=\"absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-primary/30\"\n @pointerdown=\"onResizeDown($event, col)\"\n />\n </th>\n <th v-if=\"hasActions\" class=\"w-1 px-4\" :class=\"dense ? 'py-2' : 'py-3'\" />\n </tr>\n </thead>\n\n <tbody>\n <!-- Loading -->\n <template v-if=\"loading\">\n <tr v-for=\"ri in perPage\" :key=\"`sk-${ri}`\" class=\"border-t border-outline-variant\">\n <td v-if=\"hasExpand\" :class=\"dense ? 'px-2 py-2' : 'px-2 py-3'\" />\n <td v-if=\"selectable\" :class=\"dense ? 'px-4 py-2' : 'px-4 py-3.5'\">\n <div class=\"h-4 w-4 animate-pulse rounded bg-on-surface/10\" />\n </td>\n <td v-for=\"(col, ci) in visibleColumns\" :key=\"col.key\" :class=\"dense ? 'px-3 py-2' : 'px-4 py-3.5'\">\n <div class=\"h-4 animate-pulse rounded-full bg-on-surface/10\" :style=\"{ width: skelWidth(ri, ci) }\" />\n </td>\n <td v-if=\"hasActions\" :class=\"dense ? 'px-4 py-2' : 'px-4 py-3.5'\">\n <div class=\"ml-auto h-4 w-16 animate-pulse rounded-full bg-on-surface/10\" />\n </td>\n </tr>\n </template>\n\n <!-- Empty -->\n <template v-else-if=\"visibleRows.length === 0\">\n <tr>\n <td :colspan=\"visibleColumns.length + extraCols\" class=\"border-t border-outline-variant px-4 py-14 text-center\">\n <slot name=\"empty\">\n <MIcon name=\"search_off\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ emptyText }}</p>\n </slot>\n </td>\n </tr>\n </template>\n\n <!-- Data rows -->\n <template v-else>\n <template v-for=\"row in visibleRows\" :key=\"rowId(row)\">\n <tr\n :class=\"[\n 'border-t border-outline-variant transition-colors duration-100',\n 'hover:bg-on-surface/[0.04]',\n selectable && isSelected(row) ? 'bg-primary/[0.06]' : '',\n striped ? 'even:bg-surface-container-lowest' : '',\n selectable ? 'cursor-pointer' : '',\n ]\"\n @click=\"selectable ? toggleRow(row) : emit('rowClick', row)\"\n >\n <td v-if=\"hasExpand\" class=\"px-2\" :class=\"dense ? 'py-1' : 'py-2'\" @click.stop>\n <MIconButton\n icon=\"expand_more\"\n label=\"Expandir\"\n :size=\"28\"\n :class=\"isExpanded(row) ? 'rotate-180' : ''\"\n class=\"transition-transform duration-200\"\n @click=\"toggleExpand(row)\"\n />\n </td>\n <td v-if=\"selectable\" :class=\"dense ? 'px-4 py-1' : 'px-4 py-3'\" @click.stop=\"toggleRow(row)\">\n <MCheckbox :model-value=\"isSelected(row)\" @update:model-value=\"toggleRow(row)\" />\n </td>\n <td\n v-for=\"col in visibleColumns\"\n :key=\"col.key\"\n :class=\"['text-body-medium text-on-surface', alignClass(col.align), dense ? 'px-3 py-1.5' : 'px-4 py-3']\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\" :col=\"col\">\n {{ row[col.key] ?? '—' }}\n </slot>\n </td>\n <td v-if=\"hasActions\" class=\"text-right\" :class=\"dense ? 'px-4 py-1' : 'px-4 py-3'\" @click.stop>\n <slot name=\"row-actions\" :row=\"row\" />\n </td>\n </tr>\n <!-- Expanded content -->\n <tr v-if=\"hasExpand && isExpanded(row)\">\n <td :colspan=\"visibleColumns.length + extraCols\" class=\"border-t border-outline-variant/50 bg-surface-container-lowest px-6 py-4\">\n <slot name=\"row-expand\" :row=\"row\" />\n </td>\n </tr>\n </template>\n </template>\n </tbody>\n </table>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center justify-between gap-4 border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ totalCount }} registro{{ totalCount !== 1 ? 's' : '' }}\n </span>\n <MPagination :page=\"internalPage\" :per-page=\"perPage\" :total=\"totalCount\" @update:page=\"internalPage = $event\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string | null\n label?: string\n placeholder?: string\n min?: string\n max?: string\n disabled?: boolean\n error?: string\n hint?: string\n locale?: string\n fieldBg?: string\n}>(), { locale: 'es-ES' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | null] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst viewDate = ref(props.modelValue ? new Date(props.modelValue + 'T00:00:00') : new Date())\nwatch(() => props.modelValue, (v) => {\n if (v) viewDate.value = new Date(v + 'T00:00:00')\n})\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' })\n return Array.from({ length: 7 }, (_, i) => {\n const d = new Date(2024, 0, i + 1) // Mon=1 Jan 2024\n return f.format(d)\n })\n})()\n\nconst monthLabel = computed(() => {\n const f = new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' })\n return f.format(viewDate.value)\n})\n\nconst calendarDays = computed(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: { date: number; current: boolean; iso: string; disabled: boolean }[] = []\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, disabled: isOutOfRange(iso) })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, disabled: isOutOfRange(iso) })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, disabled: isOutOfRange(iso) })\n }\n return days\n})\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n const yy = dt.getFullYear()\n const mm = String(dt.getMonth() + 1).padStart(2, '0')\n const dd = String(dt.getDate()).padStart(2, '0')\n return `${yy}-${mm}-${dd}`\n}\n\nfunction isOutOfRange(iso: string) {\n if (props.min && iso < props.min) return true\n if (props.max && iso > props.max) return true\n return false\n}\n\nconst isToday = (iso: string) => iso === fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\nfunction prevMonth() {\n const d = new Date(viewDate.value)\n d.setMonth(d.getMonth() - 1)\n viewDate.value = d\n}\nfunction nextMonth() {\n const d = new Date(viewDate.value)\n d.setMonth(d.getMonth() + 1)\n viewDate.value = d\n}\n\nfunction selectDay(day: typeof calendarDays.value[0]) {\n if (day.disabled) return\n emit('update:modelValue', day.iso)\n open.value = false\n}\n\nfunction clear() {\n emit('update:modelValue', null)\n}\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n const d = new Date(props.modelValue + 'T00:00:00')\n return new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short', year: 'numeric' }).format(d)\n})\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 380\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onClickOutside(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onClickOutside), 0)\n } else {\n document.removeEventListener('mousedown', onClickOutside)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onClickOutside)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <!-- Trigger -->\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"calendar_today\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ placeholder || label || 'Seleccionar fecha' }}</span>\n <MIcon\n v-if=\"modelValue\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <!-- Calendar dropdown -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[320px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Header -->\n <div class=\"mb-3 flex items-center justify-between\">\n <MIconButton icon=\"chevron_left\" label=\"Mes anterior\" :size=\"36\" @click=\"prevMonth\" />\n <span class=\"text-title-small font-medium capitalize text-on-surface\">{{ monthLabel }}</span>\n <MIconButton icon=\"chevron_right\" label=\"Mes siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n\n <!-- Weekday headers -->\n <div class=\"mb-1 grid grid-cols-7 gap-0.5 text-center\">\n <span v-for=\"wd in WEEKDAYS\" :key=\"wd\" class=\"py-1 text-label-small font-medium text-on-surface-variant\">\n {{ wd }}\n </span>\n </div>\n\n <!-- Days grid -->\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n class=\"flex h-9 w-full items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"[\n day.disabled\n ? 'cursor-not-allowed text-on-surface/25'\n : day.iso === modelValue\n ? 'bg-primary text-on-primary'\n : isToday(day.iso)\n ? 'border border-primary text-primary cursor-pointer hover:bg-primary/8'\n : day.current\n ? 'cursor-pointer text-on-surface hover:bg-on-surface/8'\n : 'cursor-pointer text-on-surface-variant/50 hover:bg-on-surface/4',\n ]\"\n :disabled=\"day.disabled\"\n @click=\"selectDay(day)\"\n >\n {{ day.date }}\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string | null\n label?: string\n placeholder?: string\n min?: string\n max?: string\n disabled?: boolean\n error?: string\n hint?: string\n locale?: string\n fieldBg?: string\n}>(), { locale: 'es-ES' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | null] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst viewDate = ref(props.modelValue ? new Date(props.modelValue + 'T00:00:00') : new Date())\nwatch(() => props.modelValue, (v) => {\n if (v) viewDate.value = new Date(v + 'T00:00:00')\n})\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' })\n return Array.from({ length: 7 }, (_, i) => {\n const d = new Date(2024, 0, i + 1) // Mon=1 Jan 2024\n return f.format(d)\n })\n})()\n\nconst monthLabel = computed(() => {\n const f = new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' })\n return f.format(viewDate.value)\n})\n\nconst calendarDays = computed(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: { date: number; current: boolean; iso: string; disabled: boolean }[] = []\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, disabled: isOutOfRange(iso) })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, disabled: isOutOfRange(iso) })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, disabled: isOutOfRange(iso) })\n }\n return days\n})\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n const yy = dt.getFullYear()\n const mm = String(dt.getMonth() + 1).padStart(2, '0')\n const dd = String(dt.getDate()).padStart(2, '0')\n return `${yy}-${mm}-${dd}`\n}\n\nfunction isOutOfRange(iso: string) {\n if (props.min && iso < props.min) return true\n if (props.max && iso > props.max) return true\n return false\n}\n\nconst isToday = (iso: string) => iso === fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\nfunction prevMonth() {\n const d = new Date(viewDate.value)\n d.setMonth(d.getMonth() - 1)\n viewDate.value = d\n}\nfunction nextMonth() {\n const d = new Date(viewDate.value)\n d.setMonth(d.getMonth() + 1)\n viewDate.value = d\n}\n\nfunction selectDay(day: typeof calendarDays.value[0]) {\n if (day.disabled) return\n emit('update:modelValue', day.iso)\n open.value = false\n}\n\nfunction clear() {\n emit('update:modelValue', null)\n}\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n const d = new Date(props.modelValue + 'T00:00:00')\n return new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short', year: 'numeric' }).format(d)\n})\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 380\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onClickOutside(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onClickOutside), 0)\n } else {\n document.removeEventListener('mousedown', onClickOutside)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onClickOutside)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <!-- Trigger -->\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"calendar_today\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ placeholder || label || 'Seleccionar fecha' }}</span>\n <MIcon\n v-if=\"modelValue\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <!-- Calendar dropdown -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[320px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Header -->\n <div class=\"mb-3 flex items-center justify-between\">\n <MIconButton icon=\"chevron_left\" label=\"Mes anterior\" :size=\"36\" @click=\"prevMonth\" />\n <span class=\"text-title-small font-medium capitalize text-on-surface\">{{ monthLabel }}</span>\n <MIconButton icon=\"chevron_right\" label=\"Mes siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n\n <!-- Weekday headers -->\n <div class=\"mb-1 grid grid-cols-7 gap-0.5 text-center\">\n <span v-for=\"wd in WEEKDAYS\" :key=\"wd\" class=\"py-1 text-label-small font-medium text-on-surface-variant\">\n {{ wd }}\n </span>\n </div>\n\n <!-- Days grid -->\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n class=\"flex h-9 w-full items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"[\n day.disabled\n ? 'cursor-not-allowed text-on-surface/25'\n : day.iso === modelValue\n ? 'bg-primary text-on-primary'\n : isToday(day.iso)\n ? 'border border-primary text-primary cursor-pointer hover:bg-primary/8'\n : day.current\n ? 'cursor-pointer text-on-surface hover:bg-on-surface/8'\n : 'cursor-pointer text-on-surface-variant/50 hover:bg-on-surface/4',\n ]\"\n :disabled=\"day.disabled\"\n @click=\"selectDay(day)\"\n >\n {{ day.date }}\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nexport interface DateRange {\n start: string | null\n end: string | null\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: DateRange\n label?: string\n min?: string\n max?: string\n disabled?: boolean\n error?: string\n hint?: string\n locale?: string\n fieldBg?: string\n}>(), { locale: 'es-ES' })\n\nconst emit = defineEmits<{ 'update:modelValue': [DateRange] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst picking = ref<'start' | 'end'>('start')\nconst hovered = ref<string | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst viewDate = ref(\n props.modelValue.start ? new Date(props.modelValue.start + 'T00:00:00') : new Date()\n)\nwatch(() => props.modelValue.start, (v) => {\n if (v) viewDate.value = new Date(v + 'T00:00:00')\n})\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' })\n return Array.from({ length: 7 }, (_, i) => f.format(new Date(2024, 0, i + 1)))\n})()\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(viewDate.value)\n)\n\nconst calendarDays = computed(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: { date: number; current: boolean; iso: string; disabled: boolean }[] = []\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, disabled: isOOR(iso) })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, disabled: isOOR(iso) })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, disabled: isOOR(iso) })\n }\n return days\n})\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`\n}\nfunction isOOR(iso: string) {\n if (props.min && iso < props.min) return true\n if (props.max && iso > props.max) return true\n return false\n}\nconst todayIso = fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\nfunction selectDay(day: typeof calendarDays.value[0]) {\n if (day.disabled) return\n if (picking.value === 'start') {\n emit('update:modelValue', { start: day.iso, end: null })\n picking.value = 'end'\n } else {\n const s = props.modelValue.start!\n if (day.iso < s) {\n emit('update:modelValue', { start: day.iso, end: null })\n } else {\n emit('update:modelValue', { start: s, end: day.iso })\n picking.value = 'start'\n open.value = false\n }\n }\n}\n\nfunction isInRange(iso: string) {\n const { start, end } = props.modelValue\n const effectiveEnd = end ?? hovered.value\n if (!start || !effectiveEnd) return false\n const lo = start < effectiveEnd ? start : effectiveEnd\n const hi = start < effectiveEnd ? effectiveEnd : start\n return iso > lo && iso < hi\n}\n\nfunction prevMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() - 1); viewDate.value = d }\nfunction nextMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() + 1); viewDate.value = d }\n\nconst displayValue = computed(() => {\n const f = new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short' })\n const s = props.modelValue.start ? f.format(new Date(props.modelValue.start + 'T00:00:00')) : '—'\n const e = props.modelValue.end ? f.format(new Date(props.modelValue.end + 'T00:00:00')) : '—'\n if (!props.modelValue.start && !props.modelValue.end) return ''\n return `${s} → ${e}`\n})\n\nfunction clear() { emit('update:modelValue', { start: null, end: null }); picking.value = 'start' }\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 400\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n picking.value = props.modelValue.start && !props.modelValue.end ? 'end' : 'start'\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"date_range\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ label || 'Seleccionar rango' }}</span>\n <MIcon\n v-if=\"modelValue.start || modelValue.end\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[320px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <p class=\"mb-2 text-center text-label-medium text-on-surface-variant\">\n {{ picking === 'start' ? 'Selecciona inicio' : 'Selecciona fin' }}\n </p>\n\n <div class=\"mb-3 flex items-center justify-between\">\n <MIconButton icon=\"chevron_left\" label=\"Anterior\" :size=\"36\" @click=\"prevMonth\" />\n <span class=\"text-title-small font-medium capitalize text-on-surface\">{{ monthLabel }}</span>\n <MIconButton icon=\"chevron_right\" label=\"Siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n\n <div class=\"mb-1 grid grid-cols-7 gap-0.5 text-center\">\n <span v-for=\"wd in WEEKDAYS\" :key=\"wd\" class=\"py-1 text-label-small font-medium text-on-surface-variant\">{{ wd }}</span>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n class=\"flex h-9 w-full items-center justify-center text-body-medium transition-colors duration-100\"\n :class=\"[\n day.disabled\n ? 'cursor-not-allowed text-on-surface/25 rounded-full'\n : day.iso === modelValue.start || day.iso === modelValue.end\n ? 'bg-primary text-on-primary rounded-full'\n : isInRange(day.iso)\n ? 'bg-primary/12 text-on-surface cursor-pointer'\n : day.iso === todayIso\n ? 'border border-primary text-primary rounded-full cursor-pointer hover:bg-primary/8'\n : day.current\n ? 'cursor-pointer text-on-surface rounded-full hover:bg-on-surface/8'\n : 'cursor-pointer text-on-surface-variant/50 rounded-full hover:bg-on-surface/4',\n ]\"\n :disabled=\"day.disabled\"\n @mouseenter=\"picking === 'end' && (hovered = day.iso)\"\n @click=\"selectDay(day)\"\n >\n {{ day.date }}\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nexport interface DateRange {\n start: string | null\n end: string | null\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: DateRange\n label?: string\n min?: string\n max?: string\n disabled?: boolean\n error?: string\n hint?: string\n locale?: string\n fieldBg?: string\n}>(), { locale: 'es-ES' })\n\nconst emit = defineEmits<{ 'update:modelValue': [DateRange] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst picking = ref<'start' | 'end'>('start')\nconst hovered = ref<string | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst viewDate = ref(\n props.modelValue.start ? new Date(props.modelValue.start + 'T00:00:00') : new Date()\n)\nwatch(() => props.modelValue.start, (v) => {\n if (v) viewDate.value = new Date(v + 'T00:00:00')\n})\n\nconst WEEKDAYS = (() => {\n const f = new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' })\n return Array.from({ length: 7 }, (_, i) => f.format(new Date(2024, 0, i + 1)))\n})()\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(viewDate.value)\n)\n\nconst calendarDays = computed(() => {\n const y = viewDate.value.getFullYear()\n const m = viewDate.value.getMonth()\n const first = new Date(y, m, 1)\n const startDay = (first.getDay() + 6) % 7\n const daysInMonth = new Date(y, m + 1, 0).getDate()\n const days: { date: number; current: boolean; iso: string; disabled: boolean }[] = []\n\n const prevMonth = new Date(y, m, 0).getDate()\n for (let i = startDay - 1; i >= 0; i--) {\n const d = prevMonth - i\n const iso = fmt(y, m - 1, d)\n days.push({ date: d, current: false, iso, disabled: isOOR(iso) })\n }\n for (let d = 1; d <= daysInMonth; d++) {\n const iso = fmt(y, m, d)\n days.push({ date: d, current: true, iso, disabled: isOOR(iso) })\n }\n const remaining = 42 - days.length\n for (let d = 1; d <= remaining; d++) {\n const iso = fmt(y, m + 1, d)\n days.push({ date: d, current: false, iso, disabled: isOOR(iso) })\n }\n return days\n})\n\nfunction fmt(y: number, m: number, d: number) {\n const dt = new Date(y, m, d)\n return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`\n}\nfunction isOOR(iso: string) {\n if (props.min && iso < props.min) return true\n if (props.max && iso > props.max) return true\n return false\n}\nconst todayIso = fmt(new Date().getFullYear(), new Date().getMonth(), new Date().getDate())\n\nfunction selectDay(day: typeof calendarDays.value[0]) {\n if (day.disabled) return\n if (picking.value === 'start') {\n emit('update:modelValue', { start: day.iso, end: null })\n picking.value = 'end'\n } else {\n const s = props.modelValue.start!\n if (day.iso < s) {\n emit('update:modelValue', { start: day.iso, end: null })\n } else {\n emit('update:modelValue', { start: s, end: day.iso })\n picking.value = 'start'\n open.value = false\n }\n }\n}\n\nfunction isInRange(iso: string) {\n const { start, end } = props.modelValue\n const effectiveEnd = end ?? hovered.value\n if (!start || !effectiveEnd) return false\n const lo = start < effectiveEnd ? start : effectiveEnd\n const hi = start < effectiveEnd ? effectiveEnd : start\n return iso > lo && iso < hi\n}\n\nfunction prevMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() - 1); viewDate.value = d }\nfunction nextMonth() { const d = new Date(viewDate.value); d.setMonth(d.getMonth() + 1); viewDate.value = d }\n\nconst displayValue = computed(() => {\n const f = new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short' })\n const s = props.modelValue.start ? f.format(new Date(props.modelValue.start + 'T00:00:00')) : '—'\n const e = props.modelValue.end ? f.format(new Date(props.modelValue.end + 'T00:00:00')) : '—'\n if (!props.modelValue.start && !props.modelValue.end) return ''\n return `${s} → ${e}`\n})\n\nfunction clear() { emit('update:modelValue', { start: null, end: null }); picking.value = 'start' }\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 400\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n picking.value = props.modelValue.start && !props.modelValue.end ? 'end' : 'start'\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"date_range\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ label || 'Seleccionar rango' }}</span>\n <MIcon\n v-if=\"modelValue.start || modelValue.end\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[320px] rounded-lg bg-surface-container p-4 shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <p class=\"mb-2 text-center text-label-medium text-on-surface-variant\">\n {{ picking === 'start' ? 'Selecciona inicio' : 'Selecciona fin' }}\n </p>\n\n <div class=\"mb-3 flex items-center justify-between\">\n <MIconButton icon=\"chevron_left\" label=\"Anterior\" :size=\"36\" @click=\"prevMonth\" />\n <span class=\"text-title-small font-medium capitalize text-on-surface\">{{ monthLabel }}</span>\n <MIconButton icon=\"chevron_right\" label=\"Siguiente\" :size=\"36\" @click=\"nextMonth\" />\n </div>\n\n <div class=\"mb-1 grid grid-cols-7 gap-0.5 text-center\">\n <span v-for=\"wd in WEEKDAYS\" :key=\"wd\" class=\"py-1 text-label-small font-medium text-on-surface-variant\">{{ wd }}</span>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n class=\"flex h-9 w-full items-center justify-center text-body-medium transition-colors duration-100\"\n :class=\"[\n day.disabled\n ? 'cursor-not-allowed text-on-surface/25 rounded-full'\n : day.iso === modelValue.start || day.iso === modelValue.end\n ? 'bg-primary text-on-primary rounded-full'\n : isInRange(day.iso)\n ? 'bg-primary/12 text-on-surface cursor-pointer'\n : day.iso === todayIso\n ? 'border border-primary text-primary rounded-full cursor-pointer hover:bg-primary/8'\n : day.current\n ? 'cursor-pointer text-on-surface rounded-full hover:bg-on-surface/8'\n : 'cursor-pointer text-on-surface-variant/50 rounded-full hover:bg-on-surface/4',\n ]\"\n :disabled=\"day.disabled\"\n @mouseenter=\"picking === 'end' && (hovered = day.iso)\"\n @click=\"selectDay(day)\"\n >\n {{ day.date }}\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n vertical?: boolean\n label?: string\n inset?: boolean\n}>(), { vertical: false, inset: false })\n</script>\n\n<template>\n <div\n v-if=\"!vertical\"\n class=\"flex items-center gap-3\"\n :class=\"inset && 'ml-16'\"\n role=\"separator\"\n >\n <div class=\"h-px flex-1 bg-outline-variant\" />\n <span v-if=\"label\" class=\"shrink-0 text-label-small text-on-surface-variant\">{{ label }}</span>\n <div v-if=\"label\" class=\"h-px flex-1 bg-outline-variant\" />\n </div>\n\n <div\n v-else\n class=\"w-px self-stretch bg-outline-variant\"\n role=\"separator\"\n />\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n vertical?: boolean\n label?: string\n inset?: boolean\n}>(), { vertical: false, inset: false })\n</script>\n\n<template>\n <div\n v-if=\"!vertical\"\n class=\"flex items-center gap-3\"\n :class=\"inset && 'ml-16'\"\n role=\"separator\"\n >\n <div class=\"h-px flex-1 bg-outline-variant\" />\n <span v-if=\"label\" class=\"shrink-0 text-label-small text-on-surface-variant\">{{ label }}</span>\n <div v-if=\"label\" class=\"h-px flex-1 bg-outline-variant\" />\n </div>\n\n <div\n v-else\n class=\"w-px self-stretch bg-outline-variant\"\n role=\"separator\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface DragDropItem {\n id: string | number\n [key: string]: any\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: DragDropItem[]\n handle?: boolean\n }>(),\n { handle: false },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [DragDropItem[]]\n reorder: [{ from: number; to: number; items: DragDropItem[] }]\n}>()\n\nconst dragIndex = ref<number | null>(null)\nconst overIndex = ref<number | null>(null)\n\nfunction onDragStart(e: DragEvent, index: number) {\n dragIndex.value = index\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(index))\n }\n}\n\nfunction onDragOver(e: DragEvent, index: number) {\n e.preventDefault()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overIndex.value = index\n}\n\nfunction onDragLeave() {\n overIndex.value = null\n}\n\nfunction onDrop(e: DragEvent, toIndex: number) {\n e.preventDefault()\n const fromIndex = dragIndex.value\n if (fromIndex === null || fromIndex === toIndex) {\n reset()\n return\n }\n\n const items = [...props.modelValue]\n const moved = items.splice(fromIndex, 1)[0]!\n items.splice(toIndex, 0, moved)\n\n emit('update:modelValue', items)\n emit('reorder', { from: fromIndex, to: toIndex, items })\n reset()\n}\n\nfunction onDragEnd() {\n reset()\n}\n\nfunction reset() {\n dragIndex.value = null\n overIndex.value = null\n}\n\nfunction getItemClass(index: number) {\n if (dragIndex.value === index) return 'opacity-30'\n if (overIndex.value === index && dragIndex.value !== null) return 'ring-2 ring-primary ring-inset'\n return ''\n}\n</script>\n\n<template>\n <div class=\"flex flex-col\" role=\"listbox\">\n <div\n v-for=\"(item, index) in modelValue\"\n :key=\"item.id\"\n :draggable=\"!handle\"\n class=\"group flex items-center gap-2 rounded-lg px-3 py-2 transition-all\"\n :class=\"[\n getItemClass(index),\n !handle && 'cursor-grab active:cursor-grabbing',\n ]\"\n @dragstart=\"!handle && onDragStart($event, index)\"\n @dragover=\"onDragOver($event, index)\"\n @dragleave=\"onDragLeave\"\n @drop=\"onDrop($event, index)\"\n @dragend=\"onDragEnd\"\n role=\"option\"\n >\n <div\n v-if=\"handle\"\n class=\"flex shrink-0 cursor-grab items-center justify-center rounded p-0.5 text-on-surface-variant/50 transition-colors hover:text-on-surface-variant active:cursor-grabbing\"\n draggable=\"true\"\n @dragstart=\"onDragStart($event, index)\"\n >\n <MIcon name=\"drag_indicator\" :size=\"20\" />\n </div>\n\n <div class=\"min-w-0 flex-1\">\n <slot :item=\"item\" :index=\"index\">\n <span class=\"text-body-medium text-on-surface\">{{ item.id }}</span>\n </slot>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface DragDropItem {\n id: string | number\n [key: string]: any\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: DragDropItem[]\n handle?: boolean\n }>(),\n { handle: false },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [DragDropItem[]]\n reorder: [{ from: number; to: number; items: DragDropItem[] }]\n}>()\n\nconst dragIndex = ref<number | null>(null)\nconst overIndex = ref<number | null>(null)\n\nfunction onDragStart(e: DragEvent, index: number) {\n dragIndex.value = index\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(index))\n }\n}\n\nfunction onDragOver(e: DragEvent, index: number) {\n e.preventDefault()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overIndex.value = index\n}\n\nfunction onDragLeave() {\n overIndex.value = null\n}\n\nfunction onDrop(e: DragEvent, toIndex: number) {\n e.preventDefault()\n const fromIndex = dragIndex.value\n if (fromIndex === null || fromIndex === toIndex) {\n reset()\n return\n }\n\n const items = [...props.modelValue]\n const moved = items.splice(fromIndex, 1)[0]!\n items.splice(toIndex, 0, moved)\n\n emit('update:modelValue', items)\n emit('reorder', { from: fromIndex, to: toIndex, items })\n reset()\n}\n\nfunction onDragEnd() {\n reset()\n}\n\nfunction reset() {\n dragIndex.value = null\n overIndex.value = null\n}\n\nfunction getItemClass(index: number) {\n if (dragIndex.value === index) return 'opacity-30'\n if (overIndex.value === index && dragIndex.value !== null) return 'ring-2 ring-primary ring-inset'\n return ''\n}\n</script>\n\n<template>\n <div class=\"flex flex-col\" role=\"listbox\">\n <div\n v-for=\"(item, index) in modelValue\"\n :key=\"item.id\"\n :draggable=\"!handle\"\n class=\"group flex items-center gap-2 rounded-lg px-3 py-2 transition-all\"\n :class=\"[\n getItemClass(index),\n !handle && 'cursor-grab active:cursor-grabbing',\n ]\"\n @dragstart=\"!handle && onDragStart($event, index)\"\n @dragover=\"onDragOver($event, index)\"\n @dragleave=\"onDragLeave\"\n @drop=\"onDrop($event, index)\"\n @dragend=\"onDragEnd\"\n role=\"option\"\n >\n <div\n v-if=\"handle\"\n class=\"flex shrink-0 cursor-grab items-center justify-center rounded p-0.5 text-on-surface-variant/50 transition-colors hover:text-on-surface-variant active:cursor-grabbing\"\n draggable=\"true\"\n @dragstart=\"onDragStart($event, index)\"\n >\n <MIcon name=\"drag_indicator\" :size=\"20\" />\n </div>\n\n <div class=\"min-w-0 flex-1\">\n <slot :item=\"item\" :index=\"index\">\n <span class=\"text-body-medium text-on-surface\">{{ item.id }}</span>\n </slot>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n icon?: string\n title: string\n description?: string\n compact?: boolean\n}>(), { icon: 'inbox' })\n</script>\n\n<template>\n <div\n class=\"flex flex-col items-center justify-center text-center\"\n :class=\"compact ? 'gap-2 py-6' : 'gap-3 py-14'\"\n >\n <div\n class=\"flex items-center justify-center rounded-full bg-surface-container-high text-on-surface-variant\"\n :class=\"compact ? 'h-12 w-12' : 'h-16 w-16'\"\n >\n <MIcon :name=\"icon\" :size=\"compact ? 24 : 32\" />\n </div>\n <h3\n class=\"font-medium text-on-surface\"\n :class=\"compact ? 'text-title-small' : 'text-title-medium'\"\n >\n {{ title }}\n </h3>\n <p\n v-if=\"description\"\n class=\"max-w-sm text-on-surface-variant\"\n :class=\"compact ? 'text-body-small' : 'text-body-medium'\"\n >\n {{ description }}\n </p>\n <div v-if=\"$slots.actions\" :class=\"compact ? 'mt-1' : 'mt-2'\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n icon?: string\n title: string\n description?: string\n compact?: boolean\n}>(), { icon: 'inbox' })\n</script>\n\n<template>\n <div\n class=\"flex flex-col items-center justify-center text-center\"\n :class=\"compact ? 'gap-2 py-6' : 'gap-3 py-14'\"\n >\n <div\n class=\"flex items-center justify-center rounded-full bg-surface-container-high text-on-surface-variant\"\n :class=\"compact ? 'h-12 w-12' : 'h-16 w-16'\"\n >\n <MIcon :name=\"icon\" :size=\"compact ? 24 : 32\" />\n </div>\n <h3\n class=\"font-medium text-on-surface\"\n :class=\"compact ? 'text-title-small' : 'text-title-medium'\"\n >\n {{ title }}\n </h3>\n <p\n v-if=\"description\"\n class=\"max-w-sm text-on-surface-variant\"\n :class=\"compact ? 'text-body-small' : 'text-body-medium'\"\n >\n {{ description }}\n </p>\n <div v-if=\"$slots.actions\" :class=\"compact ? 'mt-1' : 'mt-2'\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n title: string\n subtitle?: string\n icon?: string\n modelValue?: boolean\n disabled?: boolean\n variant?: 'outlined' | 'filled' | 'elevated'\n}>(), { disabled: false, variant: 'outlined' })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\nconst internal = ref(false)\nconst isOpen = computed(() =>\n props.modelValue !== undefined ? props.modelValue : internal.value,\n)\n\nfunction toggle() {\n if (props.disabled) return\n const next = !isOpen.value\n if (props.modelValue !== undefined) emit('update:modelValue', next)\n else internal.value = next\n}\n\nconst wrapperClass = computed(() => {\n if (props.variant === 'filled') return 'bg-surface-container-low rounded-md'\n if (props.variant === 'elevated') return 'bg-surface-container-low rounded-md shadow-elevation-1'\n return 'rounded-md border border-outline-variant'\n})\n</script>\n\n<template>\n <div :class=\"wrapperClass\" class=\"overflow-hidden\">\n <!-- Header / trigger -->\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-4 px-5 py-4 text-left transition-colors duration-150 focus-visible:outline-none\"\n :class=\"[\n disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer hover:bg-on-surface/4',\n isOpen ? 'bg-on-surface/4' : '',\n ]\"\n :aria-expanded=\"isOpen\"\n :disabled=\"disabled\"\n @click=\"toggle\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"22\" class=\"shrink-0 text-on-surface-variant\" />\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-body-large font-medium text-on-surface\">{{ title }}</p>\n <p v-if=\"subtitle\" class=\"text-body-small text-on-surface-variant\">{{ subtitle }}</p>\n </div>\n <MIcon\n name=\"expand_more\"\n :size=\"22\"\n class=\"shrink-0 text-on-surface-variant transition-transform duration-200\"\n :class=\"isOpen ? 'rotate-180' : ''\"\n />\n </button>\n\n <!-- Content with height animation -->\n <Transition name=\"expand\">\n <div v-if=\"isOpen\" class=\"expand-grid\">\n <div class=\"expand-body border-t border-outline-variant/60 px-5 py-4\">\n <slot />\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<style scoped>\n/*\n grid-template-rows: 0fr → 1fr expands to the exact content height,\n so the animation is always proportional — no max-height overshoot.\n*/\n.expand-grid {\n display: grid;\n grid-template-rows: 1fr;\n}\n.expand-body {\n min-height: 0; /* required for 0fr to actually collapse */\n overflow: hidden;\n}\n\n.expand-enter-active {\n transition: grid-template-rows 280ms cubic-bezier(0.2, 0, 0, 1);\n}\n.expand-enter-active > .expand-body {\n transition: opacity 220ms ease;\n}\n.expand-enter-from {\n grid-template-rows: 0fr;\n}\n.expand-enter-from > .expand-body {\n opacity: 0;\n}\n\n.expand-leave-active {\n transition: grid-template-rows 220ms cubic-bezier(0.4, 0, 1, 1);\n}\n.expand-leave-active > .expand-body {\n transition: opacity 150ms ease;\n}\n.expand-leave-to {\n grid-template-rows: 0fr;\n}\n.expand-leave-to > .expand-body {\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n title: string\n subtitle?: string\n icon?: string\n modelValue?: boolean\n disabled?: boolean\n variant?: 'outlined' | 'filled' | 'elevated'\n}>(), { disabled: false, variant: 'outlined' })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\nconst internal = ref(false)\nconst isOpen = computed(() =>\n props.modelValue !== undefined ? props.modelValue : internal.value,\n)\n\nfunction toggle() {\n if (props.disabled) return\n const next = !isOpen.value\n if (props.modelValue !== undefined) emit('update:modelValue', next)\n else internal.value = next\n}\n\nconst wrapperClass = computed(() => {\n if (props.variant === 'filled') return 'bg-surface-container-low rounded-md'\n if (props.variant === 'elevated') return 'bg-surface-container-low rounded-md shadow-elevation-1'\n return 'rounded-md border border-outline-variant'\n})\n</script>\n\n<template>\n <div :class=\"wrapperClass\" class=\"overflow-hidden\">\n <!-- Header / trigger -->\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-4 px-5 py-4 text-left transition-colors duration-150 focus-visible:outline-none\"\n :class=\"[\n disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer hover:bg-on-surface/4',\n isOpen ? 'bg-on-surface/4' : '',\n ]\"\n :aria-expanded=\"isOpen\"\n :disabled=\"disabled\"\n @click=\"toggle\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"22\" class=\"shrink-0 text-on-surface-variant\" />\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-body-large font-medium text-on-surface\">{{ title }}</p>\n <p v-if=\"subtitle\" class=\"text-body-small text-on-surface-variant\">{{ subtitle }}</p>\n </div>\n <MIcon\n name=\"expand_more\"\n :size=\"22\"\n class=\"shrink-0 text-on-surface-variant transition-transform duration-200\"\n :class=\"isOpen ? 'rotate-180' : ''\"\n />\n </button>\n\n <!-- Content with height animation -->\n <Transition name=\"expand\">\n <div v-if=\"isOpen\" class=\"expand-grid\">\n <div class=\"expand-body border-t border-outline-variant/60 px-5 py-4\">\n <slot />\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<style scoped>\n/*\n grid-template-rows: 0fr → 1fr expands to the exact content height,\n so the animation is always proportional — no max-height overshoot.\n*/\n.expand-grid {\n display: grid;\n grid-template-rows: 1fr;\n}\n.expand-body {\n min-height: 0; /* required for 0fr to actually collapse */\n overflow: hidden;\n}\n\n.expand-enter-active {\n transition: grid-template-rows 280ms cubic-bezier(0.2, 0, 0, 1);\n}\n.expand-enter-active > .expand-body {\n transition: opacity 220ms ease;\n}\n.expand-enter-from {\n grid-template-rows: 0fr;\n}\n.expand-enter-from > .expand-body {\n opacity: 0;\n}\n\n.expand-leave-active {\n transition: grid-template-rows 220ms cubic-bezier(0.4, 0, 1, 1);\n}\n.expand-leave-active > .expand-body {\n transition: opacity 150ms ease;\n}\n.expand-leave-to {\n grid-template-rows: 0fr;\n}\n.expand-leave-to > .expand-body {\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface SpeedDialItem {\n icon: string\n label?: string\n onClick?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n icon: string\n label?: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'surface'\n size?: 'small' | 'regular' | 'large'\n disabled?: boolean\n items?: SpeedDialItem[]\n direction?: 'up' | 'down' | 'left' | 'right' | 'radial'\n }>(),\n {\n color: 'primary',\n size: 'regular',\n disabled: false,\n direction: 'up',\n },\n)\n\nconst emit = defineEmits<{ click: [MouseEvent] }>()\n\nconst open = ref(false)\nconst fabEl = ref<HTMLElement>()\n\nconst hasItems = computed(() => !!props.items?.length)\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container',\n secondary: 'bg-secondary-container text-on-secondary-container',\n tertiary: 'bg-tertiary-container text-on-tertiary-container',\n surface: 'bg-surface-container-high text-primary',\n}\n\nconst fabSizeClasses = computed(() => {\n if (props.label) return 'h-14 rounded-2xl px-4 gap-3'\n switch (props.size) {\n case 'small': return 'h-10 w-10 rounded-lg'\n case 'large': return 'h-24 w-24 rounded-[28px]'\n default: return 'h-14 w-14 rounded-2xl'\n }\n})\n\nconst fabIconSize = computed(() => {\n if (props.label) return 24\n switch (props.size) {\n case 'small': return 20\n case 'large': return 36\n default: return 24\n }\n})\n\nconst fabPx = computed(() => {\n if (props.label) return 56\n switch (props.size) {\n case 'small': return 40\n case 'large': return 96\n default: return 56\n }\n})\n\nconst ITEM_PX = 40\nconst ITEM_GAP = 8\n\nfunction getRect(): DOMRect | null {\n return fabEl.value?.getBoundingClientRect() ?? null\n}\n\nfunction itemStyle(index: number): Record<string, string> {\n const rect = getRect()\n if (!rect) return { position: 'fixed', opacity: '0', pointerEvents: 'none' }\n\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n const count = props.items?.length ?? 0\n\n const delay = open.value\n ? `${index * 35}ms`\n : `${(count - 1 - index) * 35}ms`\n const transition = `transform 220ms cubic-bezier(0.2,0,0,1) ${delay}, opacity 180ms ease ${delay}`\n\n if (props.direction === 'radial') {\n const angle = (2 * Math.PI * index) / count - Math.PI / 2\n const r = 80\n const dx = (Math.cos(angle) * r).toFixed(1)\n const dy = (Math.sin(angle) * r).toFixed(1)\n return {\n position: 'fixed',\n top: `${cy - ITEM_PX / 2}px`,\n left: `${cx - ITEM_PX / 2}px`,\n transform: open.value ? `translate(${dx}px, ${dy}px) scale(1)` : 'translate(0,0) scale(0)',\n opacity: open.value ? '1' : '0',\n transition,\n pointerEvents: open.value ? 'auto' : 'none',\n zIndex: '1000',\n }\n }\n\n const step = ITEM_PX + ITEM_GAP\n const offset = fabPx.value / 2 + ITEM_GAP + ITEM_PX / 2 + index * step\n\n const posMap: Record<string, { top: string; left: string }> = {\n up: { top: `${cy - offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },\n down: { top: `${cy + offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },\n left: { top: `${cy - ITEM_PX / 2}px`, left: `${cx - offset - ITEM_PX / 2}px` },\n right: { top: `${cy - ITEM_PX / 2}px`, left: `${cx + offset - ITEM_PX / 2}px` },\n }\n\n const translateFrom: Record<string, string> = {\n up: 'translateY(12px) scale(0.75)',\n down: 'translateY(-12px) scale(0.75)',\n left: 'translateX(12px) scale(0.75)',\n right: 'translateX(-12px) scale(0.75)',\n }\n\n const pos = posMap[props.direction] ?? posMap.up\n\n return {\n position: 'fixed',\n ...pos,\n transform: open.value ? 'translate(0,0) scale(1)' : (translateFrom[props.direction] ?? 'scale(0.75)'),\n opacity: open.value ? '1' : '0',\n transition,\n pointerEvents: open.value ? 'auto' : 'none',\n zIndex: '1000',\n }\n}\n\nconst showLabel = computed(() => props.direction === 'up' || props.direction === 'down')\n\n// Force re-render to recalculate positions on scroll\nconst scrollTick = ref(0)\nfunction onScroll() {\n if (open.value) scrollTick.value++\n}\n\nfunction createRipple(event: PointerEvent | MouseEvent, target?: HTMLElement) {\n const button = (target ?? event.currentTarget) as HTMLElement\n const rect = button.getBoundingClientRect()\n const d = Math.max(rect.width, rect.height) * 2\n const el = document.createElement('span')\n el.className = 'm3-ripple'\n el.style.cssText = `width:${d}px;height:${d}px;top:${event.clientY - rect.top - d / 2}px;left:${event.clientX - rect.left - d / 2}px`\n button.appendChild(el)\n el.addEventListener('animationend', () => el.remove(), { once: true })\n}\n\nfunction handleFabClick(e: PointerEvent) {\n if (hasItems.value) {\n open.value = !open.value\n } else {\n emit('click', e)\n }\n}\n\nfunction handleItemClick(e: PointerEvent, item: SpeedDialItem, buttonEl: HTMLElement) {\n createRipple(e, buttonEl)\n open.value = false\n item.onClick?.()\n}\n\nfunction onDocClick(e: MouseEvent) {\n if (!open.value) return\n if (fabEl.value && !fabEl.value.contains(e.target as Node)) {\n open.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', onDocClick, true)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('click', onDocClick, true)\n window.removeEventListener('scroll', onScroll, true)\n})\n</script>\n\n<template>\n <div ref=\"fabEl\" class=\"relative inline-flex items-center justify-center\">\n <button\n type=\"button\"\n class=\"relative inline-flex cursor-pointer items-center justify-center overflow-hidden shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 disabled:cursor-not-allowed disabled:opacity-[0.38] before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]\"\n :class=\"[colorMap[color], fabSizeClasses]\"\n :disabled=\"disabled\"\n @pointerdown=\"(e) => { createRipple(e); handleFabClick(e) }\"\n >\n <MIcon\n :name=\"icon\"\n :size=\"fabIconSize\"\n class=\"transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)]\"\n :class=\"hasItems && open ? 'rotate-45' : ''\"\n />\n <span v-if=\"label\" class=\"text-label-large font-medium\">{{ label }}</span>\n </button>\n </div>\n\n <Teleport to=\"body\">\n <template v-if=\"hasItems\">\n <!-- hidden dep on scrollTick to force style recalc -->\n <span :data-tick=\"scrollTick\" class=\"hidden\" />\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n :style=\"itemStyle(i)\"\n class=\"flex items-center gap-3\"\n :class=\"showLabel ? 'flex-row-reverse' : ''\"\n >\n <span\n v-if=\"item.label && showLabel\"\n class=\"whitespace-nowrap rounded-md bg-surface-container-high px-3 py-1.5 text-label-medium text-on-surface shadow-elevation-1\"\n >\n {{ item.label }}\n </span>\n\n <button\n type=\"button\"\n class=\"relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]\"\n :class=\"colorMap[color]\"\n :style=\"{ width: `${ITEM_PX}px`, height: `${ITEM_PX}px` }\"\n @pointerdown=\"(e) => handleItemClick(e, item, e.currentTarget as HTMLElement)\"\n >\n <MIcon :name=\"item.icon\" :size=\"20\" />\n </button>\n </div>\n </template>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface SpeedDialItem {\n icon: string\n label?: string\n onClick?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n icon: string\n label?: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'surface'\n size?: 'small' | 'regular' | 'large'\n disabled?: boolean\n items?: SpeedDialItem[]\n direction?: 'up' | 'down' | 'left' | 'right' | 'radial'\n }>(),\n {\n color: 'primary',\n size: 'regular',\n disabled: false,\n direction: 'up',\n },\n)\n\nconst emit = defineEmits<{ click: [MouseEvent] }>()\n\nconst open = ref(false)\nconst fabEl = ref<HTMLElement>()\n\nconst hasItems = computed(() => !!props.items?.length)\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container',\n secondary: 'bg-secondary-container text-on-secondary-container',\n tertiary: 'bg-tertiary-container text-on-tertiary-container',\n surface: 'bg-surface-container-high text-primary',\n}\n\nconst fabSizeClasses = computed(() => {\n if (props.label) return 'h-14 rounded-2xl px-4 gap-3'\n switch (props.size) {\n case 'small': return 'h-10 w-10 rounded-lg'\n case 'large': return 'h-24 w-24 rounded-[28px]'\n default: return 'h-14 w-14 rounded-2xl'\n }\n})\n\nconst fabIconSize = computed(() => {\n if (props.label) return 24\n switch (props.size) {\n case 'small': return 20\n case 'large': return 36\n default: return 24\n }\n})\n\nconst fabPx = computed(() => {\n if (props.label) return 56\n switch (props.size) {\n case 'small': return 40\n case 'large': return 96\n default: return 56\n }\n})\n\nconst ITEM_PX = 40\nconst ITEM_GAP = 8\n\nfunction getRect(): DOMRect | null {\n return fabEl.value?.getBoundingClientRect() ?? null\n}\n\nfunction itemStyle(index: number): Record<string, string> {\n const rect = getRect()\n if (!rect) return { position: 'fixed', opacity: '0', pointerEvents: 'none' }\n\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n const count = props.items?.length ?? 0\n\n const delay = open.value\n ? `${index * 35}ms`\n : `${(count - 1 - index) * 35}ms`\n const transition = `transform 220ms cubic-bezier(0.2,0,0,1) ${delay}, opacity 180ms ease ${delay}`\n\n if (props.direction === 'radial') {\n const angle = (2 * Math.PI * index) / count - Math.PI / 2\n const r = 80\n const dx = (Math.cos(angle) * r).toFixed(1)\n const dy = (Math.sin(angle) * r).toFixed(1)\n return {\n position: 'fixed',\n top: `${cy - ITEM_PX / 2}px`,\n left: `${cx - ITEM_PX / 2}px`,\n transform: open.value ? `translate(${dx}px, ${dy}px) scale(1)` : 'translate(0,0) scale(0)',\n opacity: open.value ? '1' : '0',\n transition,\n pointerEvents: open.value ? 'auto' : 'none',\n zIndex: '1000',\n }\n }\n\n const step = ITEM_PX + ITEM_GAP\n const offset = fabPx.value / 2 + ITEM_GAP + ITEM_PX / 2 + index * step\n\n const posMap: Record<string, { top: string; left: string }> = {\n up: { top: `${cy - offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },\n down: { top: `${cy + offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },\n left: { top: `${cy - ITEM_PX / 2}px`, left: `${cx - offset - ITEM_PX / 2}px` },\n right: { top: `${cy - ITEM_PX / 2}px`, left: `${cx + offset - ITEM_PX / 2}px` },\n }\n\n const translateFrom: Record<string, string> = {\n up: 'translateY(12px) scale(0.75)',\n down: 'translateY(-12px) scale(0.75)',\n left: 'translateX(12px) scale(0.75)',\n right: 'translateX(-12px) scale(0.75)',\n }\n\n const pos = posMap[props.direction] ?? posMap.up\n\n return {\n position: 'fixed',\n ...pos,\n transform: open.value ? 'translate(0,0) scale(1)' : (translateFrom[props.direction] ?? 'scale(0.75)'),\n opacity: open.value ? '1' : '0',\n transition,\n pointerEvents: open.value ? 'auto' : 'none',\n zIndex: '1000',\n }\n}\n\nconst showLabel = computed(() => props.direction === 'up' || props.direction === 'down')\n\n// Force re-render to recalculate positions on scroll\nconst scrollTick = ref(0)\nfunction onScroll() {\n if (open.value) scrollTick.value++\n}\n\nfunction createRipple(event: PointerEvent | MouseEvent, target?: HTMLElement) {\n const button = (target ?? event.currentTarget) as HTMLElement\n const rect = button.getBoundingClientRect()\n const d = Math.max(rect.width, rect.height) * 2\n const el = document.createElement('span')\n el.className = 'm3-ripple'\n el.style.cssText = `width:${d}px;height:${d}px;top:${event.clientY - rect.top - d / 2}px;left:${event.clientX - rect.left - d / 2}px`\n button.appendChild(el)\n el.addEventListener('animationend', () => el.remove(), { once: true })\n}\n\nfunction handleFabClick(e: PointerEvent) {\n if (hasItems.value) {\n open.value = !open.value\n } else {\n emit('click', e)\n }\n}\n\nfunction handleItemClick(e: PointerEvent, item: SpeedDialItem, buttonEl: HTMLElement) {\n createRipple(e, buttonEl)\n open.value = false\n item.onClick?.()\n}\n\nfunction onDocClick(e: MouseEvent) {\n if (!open.value) return\n if (fabEl.value && !fabEl.value.contains(e.target as Node)) {\n open.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', onDocClick, true)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('click', onDocClick, true)\n window.removeEventListener('scroll', onScroll, true)\n})\n</script>\n\n<template>\n <div ref=\"fabEl\" class=\"relative inline-flex items-center justify-center\">\n <button\n type=\"button\"\n class=\"relative inline-flex cursor-pointer items-center justify-center overflow-hidden shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 disabled:cursor-not-allowed disabled:opacity-[0.38] before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]\"\n :class=\"[colorMap[color], fabSizeClasses]\"\n :disabled=\"disabled\"\n @pointerdown=\"(e) => { createRipple(e); handleFabClick(e) }\"\n >\n <MIcon\n :name=\"icon\"\n :size=\"fabIconSize\"\n class=\"transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)]\"\n :class=\"hasItems && open ? 'rotate-45' : ''\"\n />\n <span v-if=\"label\" class=\"text-label-large font-medium\">{{ label }}</span>\n </button>\n </div>\n\n <Teleport to=\"body\">\n <template v-if=\"hasItems\">\n <!-- hidden dep on scrollTick to force style recalc -->\n <span :data-tick=\"scrollTick\" class=\"hidden\" />\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n :style=\"itemStyle(i)\"\n class=\"flex items-center gap-3\"\n :class=\"showLabel ? 'flex-row-reverse' : ''\"\n >\n <span\n v-if=\"item.label && showLabel\"\n class=\"whitespace-nowrap rounded-md bg-surface-container-high px-3 py-1.5 text-label-medium text-on-surface shadow-elevation-1\"\n >\n {{ item.label }}\n </span>\n\n <button\n type=\"button\"\n class=\"relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]\"\n :class=\"colorMap[color]\"\n :style=\"{ width: `${ITEM_PX}px`, height: `${ITEM_PX}px` }\"\n @pointerdown=\"(e) => handleItemClick(e, item, e.currentTarget as HTMLElement)\"\n >\n <MIcon :name=\"item.icon\" :size=\"20\" />\n </button>\n </div>\n </template>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MSpinner from './MSpinner.vue'\n\nexport interface UploadFile {\n file: File\n id: string\n progress: number\n status: 'pending' | 'uploading' | 'done' | 'error'\n preview?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n accept?: string\n multiple?: boolean\n maxSize?: number\n disabled?: boolean\n }>(),\n { multiple: false, disabled: false },\n)\n\nconst emit = defineEmits<{\n select: [UploadFile[]]\n remove: [UploadFile]\n}>()\n\nconst files = ref<UploadFile[]>([])\nconst dragging = ref(false)\nconst inputRef = ref<HTMLInputElement | null>(null)\n\nconst acceptList = computed(() =>\n props.accept ? props.accept.split(',').map((s) => s.trim()) : null,\n)\n\nfunction isAccepted(file: File) {\n if (!acceptList.value) return true\n return acceptList.value.some((a) => {\n if (a.startsWith('.')) return file.name.toLowerCase().endsWith(a.toLowerCase())\n if (a.endsWith('/*')) return file.type.startsWith(a.replace('/*', '/'))\n return file.type === a\n })\n}\n\nfunction formatSize(bytes: number) {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction processFiles(fileList: FileList | File[]) {\n const arr = Array.from(fileList)\n const valid = arr.filter((f) => {\n if (!isAccepted(f)) return false\n if (props.maxSize && f.size > props.maxSize) return false\n return true\n })\n\n const entries: UploadFile[] = valid.map((f) => {\n const entry: UploadFile = {\n file: f,\n id: crypto.randomUUID(),\n progress: 0,\n status: 'pending',\n }\n if (f.type.startsWith('image/')) {\n entry.preview = URL.createObjectURL(f)\n }\n return entry\n })\n\n if (props.multiple) {\n files.value.push(...entries)\n } else {\n files.value.forEach((f) => f.preview && URL.revokeObjectURL(f.preview))\n files.value = entries.slice(0, 1)\n }\n\n emit('select', entries)\n}\n\nfunction onDrop(e: DragEvent) {\n dragging.value = false\n if (props.disabled || !e.dataTransfer?.files.length) return\n processFiles(e.dataTransfer.files)\n}\n\nfunction onFileInput(e: Event) {\n const input = e.target as HTMLInputElement\n if (input.files?.length) processFiles(input.files)\n input.value = ''\n}\n\nfunction removeFile(entry: UploadFile) {\n if (entry.preview) URL.revokeObjectURL(entry.preview)\n files.value = files.value.filter((f) => f.id !== entry.id)\n emit('remove', entry)\n}\n\nfunction openPicker() {\n if (!props.disabled) inputRef.value?.click()\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-3\">\n <!-- Drop zone -->\n <div\n class=\"relative flex min-h-[160px] cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed p-6 transition-colors duration-150\"\n :class=\"[\n disabled\n ? 'cursor-not-allowed border-outline-variant/50 bg-surface-container/30 opacity-60'\n : dragging\n ? 'border-primary bg-primary-container/20'\n : 'border-outline-variant bg-surface-container-lowest hover:border-primary/60 hover:bg-surface-container',\n ]\"\n @click=\"openPicker\"\n @dragenter.prevent=\"dragging = true\"\n @dragover.prevent=\"dragging = true\"\n @dragleave.prevent=\"dragging = false\"\n @drop.prevent=\"onDrop\"\n >\n <MIcon\n :name=\"dragging ? 'downloading' : 'cloud_upload'\"\n :size=\"40\"\n class=\"text-on-surface-variant\"\n />\n <div class=\"text-center\">\n <p class=\"text-body-large text-on-surface\">\n Arrastra archivos aquí o <span class=\"font-medium text-primary\">selecciona</span>\n </p>\n <p v-if=\"accept || maxSize\" class=\"mt-1 text-body-small text-on-surface-variant\">\n <span v-if=\"accept\">{{ accept }}</span>\n <span v-if=\"accept && maxSize\"> · </span>\n <span v-if=\"maxSize\">Máx. {{ formatSize(maxSize) }}</span>\n </p>\n </div>\n </div>\n\n <input\n ref=\"inputRef\"\n type=\"file\"\n class=\"hidden\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n @change=\"onFileInput\"\n />\n\n <!-- File list -->\n <TransitionGroup\n name=\"m3-file\"\n tag=\"div\"\n class=\"flex flex-col gap-2\"\n >\n <div\n v-for=\"entry in files\"\n :key=\"entry.id\"\n class=\"flex items-center gap-3 rounded-lg bg-surface-container p-3\"\n >\n <!-- Preview / icon -->\n <div class=\"flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-md bg-surface-container-high\">\n <img v-if=\"entry.preview\" :src=\"entry.preview\" class=\"h-full w-full object-cover\" />\n <MIcon v-else name=\"description\" :size=\"24\" class=\"text-on-surface-variant\" />\n </div>\n\n <!-- Info -->\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-body-medium text-on-surface\">{{ entry.file.name }}</p>\n <p class=\"text-body-small text-on-surface-variant\">{{ formatSize(entry.file.size) }}</p>\n <!-- Progress bar -->\n <div\n v-if=\"entry.status === 'uploading'\"\n class=\"mt-1.5 h-1 w-full overflow-hidden rounded-full bg-surface-container-highest\"\n >\n <div\n class=\"h-full rounded-full bg-primary transition-[width] duration-300\"\n :style=\"{ width: `${entry.progress}%` }\"\n />\n </div>\n </div>\n\n <!-- Status -->\n <MSpinner v-if=\"entry.status === 'uploading'\" :size=\"20\" />\n <MIcon v-else-if=\"entry.status === 'done'\" name=\"check_circle\" :size=\"20\" class=\"text-success\" />\n <MIcon v-else-if=\"entry.status === 'error'\" name=\"error\" :size=\"20\" class=\"text-error\" />\n\n <MIconButton icon=\"close\" label=\"Eliminar\" :size=\"32\" @click=\"removeFile(entry)\" />\n </div>\n </TransitionGroup>\n </div>\n</template>\n\n<style scoped>\n.m3-file-enter-active,\n.m3-file-leave-active {\n transition: all 0.2s ease;\n}\n.m3-file-enter-from,\n.m3-file-leave-to {\n opacity: 0;\n transform: translateY(-8px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MSpinner from './MSpinner.vue'\n\nexport interface UploadFile {\n file: File\n id: string\n progress: number\n status: 'pending' | 'uploading' | 'done' | 'error'\n preview?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n accept?: string\n multiple?: boolean\n maxSize?: number\n disabled?: boolean\n }>(),\n { multiple: false, disabled: false },\n)\n\nconst emit = defineEmits<{\n select: [UploadFile[]]\n remove: [UploadFile]\n}>()\n\nconst files = ref<UploadFile[]>([])\nconst dragging = ref(false)\nconst inputRef = ref<HTMLInputElement | null>(null)\n\nconst acceptList = computed(() =>\n props.accept ? props.accept.split(',').map((s) => s.trim()) : null,\n)\n\nfunction isAccepted(file: File) {\n if (!acceptList.value) return true\n return acceptList.value.some((a) => {\n if (a.startsWith('.')) return file.name.toLowerCase().endsWith(a.toLowerCase())\n if (a.endsWith('/*')) return file.type.startsWith(a.replace('/*', '/'))\n return file.type === a\n })\n}\n\nfunction formatSize(bytes: number) {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction processFiles(fileList: FileList | File[]) {\n const arr = Array.from(fileList)\n const valid = arr.filter((f) => {\n if (!isAccepted(f)) return false\n if (props.maxSize && f.size > props.maxSize) return false\n return true\n })\n\n const entries: UploadFile[] = valid.map((f) => {\n const entry: UploadFile = {\n file: f,\n id: crypto.randomUUID(),\n progress: 0,\n status: 'pending',\n }\n if (f.type.startsWith('image/')) {\n entry.preview = URL.createObjectURL(f)\n }\n return entry\n })\n\n if (props.multiple) {\n files.value.push(...entries)\n } else {\n files.value.forEach((f) => f.preview && URL.revokeObjectURL(f.preview))\n files.value = entries.slice(0, 1)\n }\n\n emit('select', entries)\n}\n\nfunction onDrop(e: DragEvent) {\n dragging.value = false\n if (props.disabled || !e.dataTransfer?.files.length) return\n processFiles(e.dataTransfer.files)\n}\n\nfunction onFileInput(e: Event) {\n const input = e.target as HTMLInputElement\n if (input.files?.length) processFiles(input.files)\n input.value = ''\n}\n\nfunction removeFile(entry: UploadFile) {\n if (entry.preview) URL.revokeObjectURL(entry.preview)\n files.value = files.value.filter((f) => f.id !== entry.id)\n emit('remove', entry)\n}\n\nfunction openPicker() {\n if (!props.disabled) inputRef.value?.click()\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-3\">\n <!-- Drop zone -->\n <div\n class=\"relative flex min-h-[160px] cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed p-6 transition-colors duration-150\"\n :class=\"[\n disabled\n ? 'cursor-not-allowed border-outline-variant/50 bg-surface-container/30 opacity-60'\n : dragging\n ? 'border-primary bg-primary-container/20'\n : 'border-outline-variant bg-surface-container-lowest hover:border-primary/60 hover:bg-surface-container',\n ]\"\n @click=\"openPicker\"\n @dragenter.prevent=\"dragging = true\"\n @dragover.prevent=\"dragging = true\"\n @dragleave.prevent=\"dragging = false\"\n @drop.prevent=\"onDrop\"\n >\n <MIcon\n :name=\"dragging ? 'downloading' : 'cloud_upload'\"\n :size=\"40\"\n class=\"text-on-surface-variant\"\n />\n <div class=\"text-center\">\n <p class=\"text-body-large text-on-surface\">\n Arrastra archivos aquí o <span class=\"font-medium text-primary\">selecciona</span>\n </p>\n <p v-if=\"accept || maxSize\" class=\"mt-1 text-body-small text-on-surface-variant\">\n <span v-if=\"accept\">{{ accept }}</span>\n <span v-if=\"accept && maxSize\"> · </span>\n <span v-if=\"maxSize\">Máx. {{ formatSize(maxSize) }}</span>\n </p>\n </div>\n </div>\n\n <input\n ref=\"inputRef\"\n type=\"file\"\n class=\"hidden\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n @change=\"onFileInput\"\n />\n\n <!-- File list -->\n <TransitionGroup\n name=\"m3-file\"\n tag=\"div\"\n class=\"flex flex-col gap-2\"\n >\n <div\n v-for=\"entry in files\"\n :key=\"entry.id\"\n class=\"flex items-center gap-3 rounded-lg bg-surface-container p-3\"\n >\n <!-- Preview / icon -->\n <div class=\"flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-md bg-surface-container-high\">\n <img v-if=\"entry.preview\" :src=\"entry.preview\" class=\"h-full w-full object-cover\" />\n <MIcon v-else name=\"description\" :size=\"24\" class=\"text-on-surface-variant\" />\n </div>\n\n <!-- Info -->\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-body-medium text-on-surface\">{{ entry.file.name }}</p>\n <p class=\"text-body-small text-on-surface-variant\">{{ formatSize(entry.file.size) }}</p>\n <!-- Progress bar -->\n <div\n v-if=\"entry.status === 'uploading'\"\n class=\"mt-1.5 h-1 w-full overflow-hidden rounded-full bg-surface-container-highest\"\n >\n <div\n class=\"h-full rounded-full bg-primary transition-[width] duration-300\"\n :style=\"{ width: `${entry.progress}%` }\"\n />\n </div>\n </div>\n\n <!-- Status -->\n <MSpinner v-if=\"entry.status === 'uploading'\" :size=\"20\" />\n <MIcon v-else-if=\"entry.status === 'done'\" name=\"check_circle\" :size=\"20\" class=\"text-success\" />\n <MIcon v-else-if=\"entry.status === 'error'\" name=\"error\" :size=\"20\" class=\"text-error\" />\n\n <MIconButton icon=\"close\" label=\"Eliminar\" :size=\"32\" @click=\"removeFile(entry)\" />\n </div>\n </TransitionGroup>\n </div>\n</template>\n\n<style scoped>\n.m3-file-enter-active,\n.m3-file-leave-active {\n transition: all 0.2s ease;\n}\n.m3-file-enter-from,\n.m3-file-leave-to {\n opacity: 0;\n transform: translateY(-8px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n cols?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n sm?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n md?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n lg?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n xl?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n alignItems?: 'start' | 'center' | 'end' | 'stretch'\n }>(),\n { cols: 1, gap: 'md', alignItems: 'stretch' },\n)\n\nconst colClasses: Record<number, string> = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-2',\n 3: 'grid-cols-3',\n 4: 'grid-cols-4',\n 5: 'grid-cols-5',\n 6: 'grid-cols-6',\n 12: 'grid-cols-12',\n}\n\nconst smColClasses: Record<number, string> = {\n 1: 'sm:grid-cols-1',\n 2: 'sm:grid-cols-2',\n 3: 'sm:grid-cols-3',\n 4: 'sm:grid-cols-4',\n 5: 'sm:grid-cols-5',\n 6: 'sm:grid-cols-6',\n 12: 'sm:grid-cols-12',\n}\n\nconst mdColClasses: Record<number, string> = {\n 1: 'md:grid-cols-1',\n 2: 'md:grid-cols-2',\n 3: 'md:grid-cols-3',\n 4: 'md:grid-cols-4',\n 5: 'md:grid-cols-5',\n 6: 'md:grid-cols-6',\n 12: 'md:grid-cols-12',\n}\n\nconst lgColClasses: Record<number, string> = {\n 1: 'lg:grid-cols-1',\n 2: 'lg:grid-cols-2',\n 3: 'lg:grid-cols-3',\n 4: 'lg:grid-cols-4',\n 5: 'lg:grid-cols-5',\n 6: 'lg:grid-cols-6',\n 12: 'lg:grid-cols-12',\n}\n\nconst xlColClasses: Record<number, string> = {\n 1: 'xl:grid-cols-1',\n 2: 'xl:grid-cols-2',\n 3: 'xl:grid-cols-3',\n 4: 'xl:grid-cols-4',\n 5: 'xl:grid-cols-5',\n 6: 'xl:grid-cols-6',\n 12: 'xl:grid-cols-12',\n}\n\nconst gapClasses: Record<string, string> = {\n none: 'gap-0',\n xs: 'gap-1',\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n xl: 'gap-8',\n}\n\nconst alignClasses: Record<string, string> = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n}\n\nconst classes = computed(() => [\n 'grid',\n colClasses[props.cols],\n props.sm && smColClasses[props.sm],\n props.md && mdColClasses[props.md],\n props.lg && lgColClasses[props.lg],\n props.xl && xlColClasses[props.xl],\n gapClasses[props.gap],\n alignClasses[props.alignItems],\n])\n</script>\n\n<template>\n <div :class=\"classes\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n cols?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n sm?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n md?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n lg?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n xl?: 1 | 2 | 3 | 4 | 5 | 6 | 12\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n alignItems?: 'start' | 'center' | 'end' | 'stretch'\n }>(),\n { cols: 1, gap: 'md', alignItems: 'stretch' },\n)\n\nconst colClasses: Record<number, string> = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-2',\n 3: 'grid-cols-3',\n 4: 'grid-cols-4',\n 5: 'grid-cols-5',\n 6: 'grid-cols-6',\n 12: 'grid-cols-12',\n}\n\nconst smColClasses: Record<number, string> = {\n 1: 'sm:grid-cols-1',\n 2: 'sm:grid-cols-2',\n 3: 'sm:grid-cols-3',\n 4: 'sm:grid-cols-4',\n 5: 'sm:grid-cols-5',\n 6: 'sm:grid-cols-6',\n 12: 'sm:grid-cols-12',\n}\n\nconst mdColClasses: Record<number, string> = {\n 1: 'md:grid-cols-1',\n 2: 'md:grid-cols-2',\n 3: 'md:grid-cols-3',\n 4: 'md:grid-cols-4',\n 5: 'md:grid-cols-5',\n 6: 'md:grid-cols-6',\n 12: 'md:grid-cols-12',\n}\n\nconst lgColClasses: Record<number, string> = {\n 1: 'lg:grid-cols-1',\n 2: 'lg:grid-cols-2',\n 3: 'lg:grid-cols-3',\n 4: 'lg:grid-cols-4',\n 5: 'lg:grid-cols-5',\n 6: 'lg:grid-cols-6',\n 12: 'lg:grid-cols-12',\n}\n\nconst xlColClasses: Record<number, string> = {\n 1: 'xl:grid-cols-1',\n 2: 'xl:grid-cols-2',\n 3: 'xl:grid-cols-3',\n 4: 'xl:grid-cols-4',\n 5: 'xl:grid-cols-5',\n 6: 'xl:grid-cols-6',\n 12: 'xl:grid-cols-12',\n}\n\nconst gapClasses: Record<string, string> = {\n none: 'gap-0',\n xs: 'gap-1',\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n xl: 'gap-8',\n}\n\nconst alignClasses: Record<string, string> = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n}\n\nconst classes = computed(() => [\n 'grid',\n colClasses[props.cols],\n props.sm && smColClasses[props.sm],\n props.md && mdColClasses[props.md],\n props.lg && lgColClasses[props.lg],\n props.xl && xlColClasses[props.xl],\n gapClasses[props.gap],\n alignClasses[props.alignItems],\n])\n</script>\n\n<template>\n <div :class=\"classes\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onMounted, onBeforeUnmount } from 'vue'\n\nexport interface HotkeyBinding {\n keys: string\n label: string\n handler: () => void\n group?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n bindings: HotkeyBinding[]\n showOverlay?: boolean\n }>(),\n { showOverlay: false },\n)\n\nconst isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.userAgent)\n\nfunction formatKey(raw: string): string {\n return raw\n .replace(/mod/gi, isMac ? '⌘' : 'Ctrl')\n .replace(/ctrl/gi, isMac ? '⌃' : 'Ctrl')\n .replace(/alt/gi, isMac ? '⌥' : 'Alt')\n .replace(/shift/gi, isMac ? '⇧' : 'Shift')\n .replace(/meta/gi, '⌘')\n .replace(/enter/gi, '↵')\n .replace(/escape/gi, 'Esc')\n .replace(/backspace/gi, '⌫')\n .replace(/delete/gi, '⌦')\n .replace(/arrowup/gi, '↑')\n .replace(/arrowdown/gi, '↓')\n .replace(/arrowleft/gi, '←')\n .replace(/arrowright/gi, '→')\n}\n\nfunction parseCombo(keys: string) {\n return keys.split('+').map(k => k.trim().toLowerCase())\n}\n\nfunction matchesEvent(combo: string[], e: KeyboardEvent): boolean {\n const modifiers = { ctrl: e.ctrlKey, alt: e.altKey, shift: e.shiftKey, meta: e.metaKey, mod: isMac ? e.metaKey : e.ctrlKey }\n const key = e.key.toLowerCase()\n\n for (const part of combo) {\n if (part in modifiers) {\n if (!modifiers[part as keyof typeof modifiers]) return false\n } else if (key !== part) {\n return false\n }\n }\n\n for (const [mod, active] of Object.entries(modifiers)) {\n if (active && !combo.includes(mod)) return false\n }\n\n return true\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n const tag = (e.target as HTMLElement).tagName\n const editable = tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable\n\n for (const binding of props.bindings) {\n if (binding.disabled) continue\n const combo = parseCombo(binding.keys)\n const hasModifier = combo.some(k => ['ctrl', 'alt', 'shift', 'meta', 'mod'].includes(k))\n if (!hasModifier && editable) continue\n if (matchesEvent(combo, e)) {\n e.preventDefault()\n binding.handler()\n return\n }\n }\n}\n\nonMounted(() => document.addEventListener('keydown', onKeydown))\nonBeforeUnmount(() => document.removeEventListener('keydown', onKeydown))\n\nconst grouped = () => {\n const map = new Map<string, HotkeyBinding[]>()\n for (const b of props.bindings) {\n const g = b.group ?? ''\n if (!map.has(g)) map.set(g, [])\n map.get(g)!.push(b)\n }\n return map\n}\n</script>\n\n<template>\n <div v-if=\"showOverlay\" class=\"flex flex-col gap-4\">\n <template v-for=\"[group, bindings] in grouped()\" :key=\"group\">\n <div>\n <p v-if=\"group\" class=\"mb-2 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ group }}\n </p>\n <div class=\"flex flex-col gap-1\">\n <div\n v-for=\"b in bindings\"\n :key=\"b.keys\"\n class=\"flex items-center justify-between rounded-lg px-3 py-2 transition-colors hover:bg-on-surface/4\"\n :class=\"b.disabled && 'opacity-38'\"\n >\n <span class=\"text-body-medium text-on-surface\">{{ b.label }}</span>\n <div class=\"flex items-center gap-0.5\">\n <kbd\n v-for=\"(k, ki) in b.keys.split('+')\"\n :key=\"ki\"\n class=\"inline-flex min-w-[24px] items-center justify-center rounded bg-surface-container px-1.5 py-0.5 text-center text-label-small font-medium text-on-surface-variant\"\n >\n {{ formatKey(k.trim()) }}\n </kbd>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onMounted, onBeforeUnmount } from 'vue'\n\nexport interface HotkeyBinding {\n keys: string\n label: string\n handler: () => void\n group?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n bindings: HotkeyBinding[]\n showOverlay?: boolean\n }>(),\n { showOverlay: false },\n)\n\nconst isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.userAgent)\n\nfunction formatKey(raw: string): string {\n return raw\n .replace(/mod/gi, isMac ? '⌘' : 'Ctrl')\n .replace(/ctrl/gi, isMac ? '⌃' : 'Ctrl')\n .replace(/alt/gi, isMac ? '⌥' : 'Alt')\n .replace(/shift/gi, isMac ? '⇧' : 'Shift')\n .replace(/meta/gi, '⌘')\n .replace(/enter/gi, '↵')\n .replace(/escape/gi, 'Esc')\n .replace(/backspace/gi, '⌫')\n .replace(/delete/gi, '⌦')\n .replace(/arrowup/gi, '↑')\n .replace(/arrowdown/gi, '↓')\n .replace(/arrowleft/gi, '←')\n .replace(/arrowright/gi, '→')\n}\n\nfunction parseCombo(keys: string) {\n return keys.split('+').map(k => k.trim().toLowerCase())\n}\n\nfunction matchesEvent(combo: string[], e: KeyboardEvent): boolean {\n const modifiers = { ctrl: e.ctrlKey, alt: e.altKey, shift: e.shiftKey, meta: e.metaKey, mod: isMac ? e.metaKey : e.ctrlKey }\n const key = e.key.toLowerCase()\n\n for (const part of combo) {\n if (part in modifiers) {\n if (!modifiers[part as keyof typeof modifiers]) return false\n } else if (key !== part) {\n return false\n }\n }\n\n for (const [mod, active] of Object.entries(modifiers)) {\n if (active && !combo.includes(mod)) return false\n }\n\n return true\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n const tag = (e.target as HTMLElement).tagName\n const editable = tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable\n\n for (const binding of props.bindings) {\n if (binding.disabled) continue\n const combo = parseCombo(binding.keys)\n const hasModifier = combo.some(k => ['ctrl', 'alt', 'shift', 'meta', 'mod'].includes(k))\n if (!hasModifier && editable) continue\n if (matchesEvent(combo, e)) {\n e.preventDefault()\n binding.handler()\n return\n }\n }\n}\n\nonMounted(() => document.addEventListener('keydown', onKeydown))\nonBeforeUnmount(() => document.removeEventListener('keydown', onKeydown))\n\nconst grouped = () => {\n const map = new Map<string, HotkeyBinding[]>()\n for (const b of props.bindings) {\n const g = b.group ?? ''\n if (!map.has(g)) map.set(g, [])\n map.get(g)!.push(b)\n }\n return map\n}\n</script>\n\n<template>\n <div v-if=\"showOverlay\" class=\"flex flex-col gap-4\">\n <template v-for=\"[group, bindings] in grouped()\" :key=\"group\">\n <div>\n <p v-if=\"group\" class=\"mb-2 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ group }}\n </p>\n <div class=\"flex flex-col gap-1\">\n <div\n v-for=\"b in bindings\"\n :key=\"b.keys\"\n class=\"flex items-center justify-between rounded-lg px-3 py-2 transition-colors hover:bg-on-surface/4\"\n :class=\"b.disabled && 'opacity-38'\"\n >\n <span class=\"text-body-medium text-on-surface\">{{ b.label }}</span>\n <div class=\"flex items-center gap-0.5\">\n <kbd\n v-for=\"(k, ki) in b.keys.split('+')\"\n :key=\"ki\"\n class=\"inline-flex min-w-[24px] items-center justify-center rounded bg-surface-container px-1.5 py-0.5 text-center text-label-small font-medium text-on-surface-variant\"\n >\n {{ formatKey(k.trim()) }}\n </kbd>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, onBeforeUnmount, watch } from 'vue'\nimport MSpinner from './MSpinner.vue'\n\nconst props = withDefaults(\n defineProps<{\n loading?: boolean\n disabled?: boolean\n threshold?: number\n loadingText?: string\n endText?: string\n ended?: boolean\n }>(),\n {\n loading: false,\n disabled: false,\n threshold: 100,\n loadingText: 'Cargando...',\n endText: 'No hay más elementos',\n ended: false,\n },\n)\n\nconst emit = defineEmits<{ load: [] }>()\n\nconst sentinelRef = ref<HTMLElement | null>(null)\nlet observer: IntersectionObserver | null = null\n\nfunction createObserver() {\n if (observer) observer.disconnect()\n if (props.disabled || props.ended) return\n\n observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0]\n if (entry?.isIntersecting && !props.loading && !props.ended && !props.disabled) {\n emit('load')\n }\n },\n { rootMargin: `0px 0px ${props.threshold}px 0px` },\n )\n\n if (sentinelRef.value) observer.observe(sentinelRef.value)\n}\n\nonMounted(createObserver)\n\nwatch(() => [props.disabled, props.ended], createObserver)\n\nonBeforeUnmount(() => observer?.disconnect())\n</script>\n\n<template>\n <div>\n <slot />\n\n <div ref=\"sentinelRef\" class=\"flex items-center justify-center py-4\">\n <div v-if=\"loading\" class=\"flex items-center gap-3\">\n <MSpinner :size=\"20\" class=\"text-primary\" />\n <span class=\"text-body-medium text-on-surface-variant\">{{ loadingText }}</span>\n </div>\n <p v-else-if=\"ended\" class=\"text-body-small text-on-surface-variant\">\n {{ endText }}\n </p>\n <slot v-else name=\"idle\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, onMounted, onBeforeUnmount, watch } from 'vue'\nimport MSpinner from './MSpinner.vue'\n\nconst props = withDefaults(\n defineProps<{\n loading?: boolean\n disabled?: boolean\n threshold?: number\n loadingText?: string\n endText?: string\n ended?: boolean\n }>(),\n {\n loading: false,\n disabled: false,\n threshold: 100,\n loadingText: 'Cargando...',\n endText: 'No hay más elementos',\n ended: false,\n },\n)\n\nconst emit = defineEmits<{ load: [] }>()\n\nconst sentinelRef = ref<HTMLElement | null>(null)\nlet observer: IntersectionObserver | null = null\n\nfunction createObserver() {\n if (observer) observer.disconnect()\n if (props.disabled || props.ended) return\n\n observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0]\n if (entry?.isIntersecting && !props.loading && !props.ended && !props.disabled) {\n emit('load')\n }\n },\n { rootMargin: `0px 0px ${props.threshold}px 0px` },\n )\n\n if (sentinelRef.value) observer.observe(sentinelRef.value)\n}\n\nonMounted(createObserver)\n\nwatch(() => [props.disabled, props.ended], createObserver)\n\nonBeforeUnmount(() => observer?.disconnect())\n</script>\n\n<template>\n <div>\n <slot />\n\n <div ref=\"sentinelRef\" class=\"flex items-center justify-center py-4\">\n <div v-if=\"loading\" class=\"flex items-center gap-3\">\n <MSpinner :size=\"20\" class=\"text-primary\" />\n <span class=\"text-body-medium text-on-surface-variant\">{{ loadingText }}</span>\n </div>\n <p v-else-if=\"ended\" class=\"text-body-small text-on-surface-variant\">\n {{ endText }}\n </p>\n <slot v-else name=\"idle\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n data: unknown\n rootName?: string\n expandDepth?: number\n /** @internal — used by recursive instances */\n _depth?: number\n }>(),\n { rootName: 'root', expandDepth: 2, _depth: 0 },\n)\n\nconst expanded = ref(props._depth < props.expandDepth)\n\nconst dataType = computed(() => {\n if (props.data === null) return 'null'\n if (Array.isArray(props.data)) return 'array'\n return typeof props.data\n})\n\nconst isExpandable = computed(() => dataType.value === 'object' || dataType.value === 'array')\n\nconst entries = computed(() => {\n if (dataType.value === 'array') {\n return (props.data as unknown[]).map((v, i) => ({ key: String(i), value: v }))\n }\n if (dataType.value === 'object' && props.data) {\n return Object.entries(props.data as Record<string, unknown>).map(([k, v]) => ({ key: k, value: v }))\n }\n return []\n})\n\nconst childCount = computed(() => entries.value.length)\n\nconst bracketOpen = computed(() => (dataType.value === 'array' ? '[' : '{'))\nconst bracketClose = computed(() => (dataType.value === 'array' ? ']' : '}'))\n\nfunction valueClass(val: unknown) {\n if (val === null) return 'text-on-surface-variant italic'\n switch (typeof val) {\n case 'string': return 'text-success'\n case 'number': return 'text-primary'\n case 'boolean': return 'text-tertiary'\n default: return 'text-on-surface'\n }\n}\n\nfunction formatValue(val: unknown) {\n if (typeof val === 'string') return `\"${val}\"`\n if (val === null) return 'null'\n if (val === undefined) return 'undefined'\n return String(val)\n}\n</script>\n\n<template>\n <div class=\"font-mono text-body-small leading-relaxed\" :class=\"{ 'rounded-lg border border-outline-variant bg-surface-container-lowest p-3': _depth === 0 }\">\n <!-- Expandable node -->\n <template v-if=\"isExpandable\">\n <button\n type=\"button\"\n class=\"group inline-flex cursor-pointer items-center gap-0.5 rounded px-0.5 hover:bg-on-surface/[0.06]\"\n @click=\"expanded = !expanded\"\n >\n <MIcon\n :name=\"expanded ? 'expand_more' : 'chevron_right'\"\n :size=\"16\"\n class=\"text-on-surface-variant transition-transform duration-100\"\n />\n <span v-if=\"_depth === 0 || rootName\" class=\"text-tertiary\">{{ _depth === 0 ? rootName : '' }}</span>\n <span class=\"text-on-surface-variant\">{{ bracketOpen }}</span>\n <span v-if=\"!expanded\" class=\"text-on-surface-variant/60\">\n {{ childCount }} {{ dataType === 'array' ? 'elementos' : 'campos' }}\n </span>\n <span v-if=\"!expanded\" class=\"text-on-surface-variant\">{{ bracketClose }}</span>\n </button>\n\n <div v-if=\"expanded\" class=\"ml-5 border-l border-outline-variant/40 pl-2\">\n <div v-for=\"entry in entries\" :key=\"entry.key\" class=\"flex items-start\">\n <span class=\"shrink-0 text-primary\">{{ dataType === 'array' ? '' : `\"${entry.key}\"` }}</span>\n <span v-if=\"dataType !== 'array'\" class=\"shrink-0 text-on-surface-variant mr-1\">:</span>\n\n <!-- Recursive child -->\n <MJsonViewer\n v-if=\"entry.value !== null && (typeof entry.value === 'object')\"\n :data=\"entry.value\"\n :root-name=\"entry.key\"\n :expand-depth=\"expandDepth\"\n :_depth=\"_depth + 1\"\n />\n <!-- Primitive value -->\n <span v-else :class=\"valueClass(entry.value)\">{{ formatValue(entry.value) }}</span>\n </div>\n </div>\n <span v-if=\"expanded\" class=\"ml-5 text-on-surface-variant\">{{ bracketClose }}</span>\n </template>\n\n <!-- Primitive root -->\n <template v-else>\n <span :class=\"valueClass(data)\">{{ formatValue(data) }}</span>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{\n data: unknown\n rootName?: string\n expandDepth?: number\n /** @internal — used by recursive instances */\n _depth?: number\n }>(),\n { rootName: 'root', expandDepth: 2, _depth: 0 },\n)\n\nconst expanded = ref(props._depth < props.expandDepth)\n\nconst dataType = computed(() => {\n if (props.data === null) return 'null'\n if (Array.isArray(props.data)) return 'array'\n return typeof props.data\n})\n\nconst isExpandable = computed(() => dataType.value === 'object' || dataType.value === 'array')\n\nconst entries = computed(() => {\n if (dataType.value === 'array') {\n return (props.data as unknown[]).map((v, i) => ({ key: String(i), value: v }))\n }\n if (dataType.value === 'object' && props.data) {\n return Object.entries(props.data as Record<string, unknown>).map(([k, v]) => ({ key: k, value: v }))\n }\n return []\n})\n\nconst childCount = computed(() => entries.value.length)\n\nconst bracketOpen = computed(() => (dataType.value === 'array' ? '[' : '{'))\nconst bracketClose = computed(() => (dataType.value === 'array' ? ']' : '}'))\n\nfunction valueClass(val: unknown) {\n if (val === null) return 'text-on-surface-variant italic'\n switch (typeof val) {\n case 'string': return 'text-success'\n case 'number': return 'text-primary'\n case 'boolean': return 'text-tertiary'\n default: return 'text-on-surface'\n }\n}\n\nfunction formatValue(val: unknown) {\n if (typeof val === 'string') return `\"${val}\"`\n if (val === null) return 'null'\n if (val === undefined) return 'undefined'\n return String(val)\n}\n</script>\n\n<template>\n <div class=\"font-mono text-body-small leading-relaxed\" :class=\"{ 'rounded-lg border border-outline-variant bg-surface-container-lowest p-3': _depth === 0 }\">\n <!-- Expandable node -->\n <template v-if=\"isExpandable\">\n <button\n type=\"button\"\n class=\"group inline-flex cursor-pointer items-center gap-0.5 rounded px-0.5 hover:bg-on-surface/[0.06]\"\n @click=\"expanded = !expanded\"\n >\n <MIcon\n :name=\"expanded ? 'expand_more' : 'chevron_right'\"\n :size=\"16\"\n class=\"text-on-surface-variant transition-transform duration-100\"\n />\n <span v-if=\"_depth === 0 || rootName\" class=\"text-tertiary\">{{ _depth === 0 ? rootName : '' }}</span>\n <span class=\"text-on-surface-variant\">{{ bracketOpen }}</span>\n <span v-if=\"!expanded\" class=\"text-on-surface-variant/60\">\n {{ childCount }} {{ dataType === 'array' ? 'elementos' : 'campos' }}\n </span>\n <span v-if=\"!expanded\" class=\"text-on-surface-variant\">{{ bracketClose }}</span>\n </button>\n\n <div v-if=\"expanded\" class=\"ml-5 border-l border-outline-variant/40 pl-2\">\n <div v-for=\"entry in entries\" :key=\"entry.key\" class=\"flex items-start\">\n <span class=\"shrink-0 text-primary\">{{ dataType === 'array' ? '' : `\"${entry.key}\"` }}</span>\n <span v-if=\"dataType !== 'array'\" class=\"shrink-0 text-on-surface-variant mr-1\">:</span>\n\n <!-- Recursive child -->\n <MJsonViewer\n v-if=\"entry.value !== null && (typeof entry.value === 'object')\"\n :data=\"entry.value\"\n :root-name=\"entry.key\"\n :expand-depth=\"expandDepth\"\n :_depth=\"_depth + 1\"\n />\n <!-- Primitive value -->\n <span v-else :class=\"valueClass(entry.value)\">{{ formatValue(entry.value) }}</span>\n </div>\n </div>\n <span v-if=\"expanded\" class=\"ml-5 text-on-surface-variant\">{{ bracketClose }}</span>\n </template>\n\n <!-- Primitive root -->\n <template v-else>\n <span :class=\"valueClass(data)\">{{ formatValue(data) }}</span>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface KanbanCard {\n id: string | number\n [key: string]: any\n}\n\nexport interface KanbanColumn {\n id: string | number\n title: string\n cards: KanbanCard[]\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n}\n\nconst props = defineProps<{\n modelValue: KanbanColumn[]\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [KanbanColumn[]]\n cardMove: [{ cardId: string | number; fromColumn: string | number; toColumn: string | number; toIndex: number }]\n cardClick: [{ card: KanbanCard; columnId: string | number }]\n}>()\n\nconst dragCard = ref<{ cardId: string | number; columnId: string | number } | null>(null)\nconst overColumn = ref<string | number | null>(null)\nconst overCardIndex = ref<number | null>(null)\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary',\n secondary: 'bg-secondary',\n tertiary: 'bg-tertiary',\n error: 'bg-error',\n success: 'bg-success',\n}\n\nfunction onCardDragStart(e: DragEvent, card: KanbanCard, columnId: string | number) {\n dragCard.value = { cardId: card.id, columnId }\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(card.id))\n }\n}\n\nfunction onColumnDragOver(e: DragEvent, columnId: string | number) {\n e.preventDefault()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overColumn.value = columnId\n}\n\nfunction onCardDragOver(e: DragEvent, _card: KanbanCard, index: number, columnId: string | number) {\n e.preventDefault()\n e.stopPropagation()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overColumn.value = columnId\n overCardIndex.value = index\n}\n\nfunction onDrop(e: DragEvent, toColumnId: string | number) {\n e.preventDefault()\n if (!dragCard.value) return\n\n const { cardId, columnId: fromColumnId } = dragCard.value\n if (fromColumnId === toColumnId && overCardIndex.value === null) {\n reset()\n return\n }\n\n const columns = props.modelValue.map((col) => ({ ...col, cards: [...col.cards] }))\n const fromCol = columns.find((c) => c.id === fromColumnId)\n const toCol = columns.find((c) => c.id === toColumnId)\n if (!fromCol || !toCol) { reset(); return }\n\n const cardIndex = fromCol.cards.findIndex((c) => c.id === cardId)\n if (cardIndex === -1) { reset(); return }\n\n const removed = fromCol.cards.splice(cardIndex, 1)\n const toIndex = overCardIndex.value ?? toCol.cards.length\n toCol.cards.splice(toIndex, 0, removed[0]!)\n\n emit('update:modelValue', columns)\n emit('cardMove', { cardId, fromColumn: fromColumnId, toColumn: toColumnId, toIndex })\n reset()\n}\n\nfunction reset() {\n dragCard.value = null\n overColumn.value = null\n overCardIndex.value = null\n}\n</script>\n\n<template>\n <div class=\"flex gap-4 overflow-x-auto pb-2\">\n <div\n v-for=\"column in modelValue\"\n :key=\"column.id\"\n class=\"flex w-72 shrink-0 flex-col rounded-xl bg-surface-container-low\"\n :class=\"overColumn === column.id && dragCard ? 'ring-2 ring-primary ring-inset' : ''\"\n @dragover=\"onColumnDragOver($event, column.id)\"\n @dragleave=\"overColumn = null\"\n @drop=\"onDrop($event, column.id)\"\n >\n <!-- Column header -->\n <div class=\"flex items-center gap-2 px-4 py-3\">\n <div v-if=\"column.color\" class=\"h-2.5 w-2.5 rounded-full\" :class=\"colorMap[column.color] ?? 'bg-primary'\" />\n <h3 class=\"flex-1 text-title-small font-medium text-on-surface\">{{ column.title }}</h3>\n <span class=\"rounded-full bg-surface-container-high px-2 py-0.5 text-label-small text-on-surface-variant\">\n {{ column.cards.length }}\n </span>\n </div>\n\n <!-- Cards area -->\n <div class=\"flex min-h-[60px] flex-1 flex-col gap-2 px-3 pb-3\">\n <div\n v-for=\"(card, index) in column.cards\"\n :key=\"card.id\"\n draggable=\"true\"\n class=\"cursor-grab rounded-lg bg-surface p-3 shadow-elevation-1 transition-all duration-150 active:cursor-grabbing\"\n :class=\"[\n dragCard?.cardId === card.id ? 'opacity-30' : 'hover:shadow-elevation-2',\n overCardIndex === index && overColumn === column.id && dragCard ? 'border-t-2 border-primary' : '',\n ]\"\n @dragstart=\"onCardDragStart($event, card, column.id)\"\n @dragover=\"onCardDragOver($event, card, index, column.id)\"\n @dragend=\"reset\"\n @click=\"emit('cardClick', { card, columnId: column.id })\"\n >\n <slot name=\"card\" :card=\"card\" :column=\"column\">\n <p class=\"text-body-medium text-on-surface\">{{ card.id }}</p>\n </slot>\n </div>\n\n <!-- Empty state -->\n <div\n v-if=\"column.cards.length === 0\"\n class=\"flex flex-1 items-center justify-center rounded-lg border border-dashed border-outline-variant/50 p-4\"\n >\n <p class=\"text-body-small text-on-surface-variant/60\">Sin tarjetas</p>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface KanbanCard {\n id: string | number\n [key: string]: any\n}\n\nexport interface KanbanColumn {\n id: string | number\n title: string\n cards: KanbanCard[]\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n}\n\nconst props = defineProps<{\n modelValue: KanbanColumn[]\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [KanbanColumn[]]\n cardMove: [{ cardId: string | number; fromColumn: string | number; toColumn: string | number; toIndex: number }]\n cardClick: [{ card: KanbanCard; columnId: string | number }]\n}>()\n\nconst dragCard = ref<{ cardId: string | number; columnId: string | number } | null>(null)\nconst overColumn = ref<string | number | null>(null)\nconst overCardIndex = ref<number | null>(null)\n\nconst colorMap: Record<string, string> = {\n primary: 'bg-primary',\n secondary: 'bg-secondary',\n tertiary: 'bg-tertiary',\n error: 'bg-error',\n success: 'bg-success',\n}\n\nfunction onCardDragStart(e: DragEvent, card: KanbanCard, columnId: string | number) {\n dragCard.value = { cardId: card.id, columnId }\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(card.id))\n }\n}\n\nfunction onColumnDragOver(e: DragEvent, columnId: string | number) {\n e.preventDefault()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overColumn.value = columnId\n}\n\nfunction onCardDragOver(e: DragEvent, _card: KanbanCard, index: number, columnId: string | number) {\n e.preventDefault()\n e.stopPropagation()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'\n overColumn.value = columnId\n overCardIndex.value = index\n}\n\nfunction onDrop(e: DragEvent, toColumnId: string | number) {\n e.preventDefault()\n if (!dragCard.value) return\n\n const { cardId, columnId: fromColumnId } = dragCard.value\n if (fromColumnId === toColumnId && overCardIndex.value === null) {\n reset()\n return\n }\n\n const columns = props.modelValue.map((col) => ({ ...col, cards: [...col.cards] }))\n const fromCol = columns.find((c) => c.id === fromColumnId)\n const toCol = columns.find((c) => c.id === toColumnId)\n if (!fromCol || !toCol) { reset(); return }\n\n const cardIndex = fromCol.cards.findIndex((c) => c.id === cardId)\n if (cardIndex === -1) { reset(); return }\n\n const removed = fromCol.cards.splice(cardIndex, 1)\n const toIndex = overCardIndex.value ?? toCol.cards.length\n toCol.cards.splice(toIndex, 0, removed[0]!)\n\n emit('update:modelValue', columns)\n emit('cardMove', { cardId, fromColumn: fromColumnId, toColumn: toColumnId, toIndex })\n reset()\n}\n\nfunction reset() {\n dragCard.value = null\n overColumn.value = null\n overCardIndex.value = null\n}\n</script>\n\n<template>\n <div class=\"flex gap-4 overflow-x-auto pb-2\">\n <div\n v-for=\"column in modelValue\"\n :key=\"column.id\"\n class=\"flex w-72 shrink-0 flex-col rounded-xl bg-surface-container-low\"\n :class=\"overColumn === column.id && dragCard ? 'ring-2 ring-primary ring-inset' : ''\"\n @dragover=\"onColumnDragOver($event, column.id)\"\n @dragleave=\"overColumn = null\"\n @drop=\"onDrop($event, column.id)\"\n >\n <!-- Column header -->\n <div class=\"flex items-center gap-2 px-4 py-3\">\n <div v-if=\"column.color\" class=\"h-2.5 w-2.5 rounded-full\" :class=\"colorMap[column.color] ?? 'bg-primary'\" />\n <h3 class=\"flex-1 text-title-small font-medium text-on-surface\">{{ column.title }}</h3>\n <span class=\"rounded-full bg-surface-container-high px-2 py-0.5 text-label-small text-on-surface-variant\">\n {{ column.cards.length }}\n </span>\n </div>\n\n <!-- Cards area -->\n <div class=\"flex min-h-[60px] flex-1 flex-col gap-2 px-3 pb-3\">\n <div\n v-for=\"(card, index) in column.cards\"\n :key=\"card.id\"\n draggable=\"true\"\n class=\"cursor-grab rounded-lg bg-surface p-3 shadow-elevation-1 transition-all duration-150 active:cursor-grabbing\"\n :class=\"[\n dragCard?.cardId === card.id ? 'opacity-30' : 'hover:shadow-elevation-2',\n overCardIndex === index && overColumn === column.id && dragCard ? 'border-t-2 border-primary' : '',\n ]\"\n @dragstart=\"onCardDragStart($event, card, column.id)\"\n @dragover=\"onCardDragOver($event, card, index, column.id)\"\n @dragend=\"reset\"\n @click=\"emit('cardClick', { card, columnId: column.id })\"\n >\n <slot name=\"card\" :card=\"card\" :column=\"column\">\n <p class=\"text-body-medium text-on-surface\">{{ card.id }}</p>\n </slot>\n </div>\n\n <!-- Empty state -->\n <div\n v-if=\"column.cards.length === 0\"\n class=\"flex flex-1 items-center justify-center rounded-lg border border-dashed border-outline-variant/50 p-4\"\n >\n <p class=\"text-body-small text-on-surface-variant/60\">Sin tarjetas</p>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MSpinner from './MSpinner.vue'\n\nwithDefaults(defineProps<{\n visible: boolean\n text?: string\n fullscreen?: boolean\n opaque?: boolean\n spinnerSize?: number\n}>(), { fullscreen: false, opaque: false, spinnerSize: 40 })\n</script>\n\n<template>\n <Teleport v-if=\"fullscreen\" to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-200\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-150\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"fixed inset-0 z-[300] flex flex-col items-center justify-center gap-4\"\n :class=\"opaque ? 'bg-surface' : 'bg-surface/80 backdrop-blur-sm'\"\n >\n <MSpinner :size=\"spinnerSize\" class=\"text-primary\" />\n <p v-if=\"text\" class=\"text-body-large text-on-surface-variant\">{{ text }}</p>\n <slot />\n </div>\n </Transition>\n </Teleport>\n\n <div v-else class=\"relative\">\n <slot name=\"content\" />\n <Transition\n enter-active-class=\"transition-opacity duration-200\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-150\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 rounded-[inherit]\"\n :class=\"opaque ? 'bg-surface' : 'bg-surface/80 backdrop-blur-sm'\"\n >\n <MSpinner :size=\"spinnerSize\" class=\"text-primary\" />\n <p v-if=\"text\" class=\"text-body-medium text-on-surface-variant\">{{ text }}</p>\n <slot />\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MSpinner from './MSpinner.vue'\n\nwithDefaults(defineProps<{\n visible: boolean\n text?: string\n fullscreen?: boolean\n opaque?: boolean\n spinnerSize?: number\n}>(), { fullscreen: false, opaque: false, spinnerSize: 40 })\n</script>\n\n<template>\n <Teleport v-if=\"fullscreen\" to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-200\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-150\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"fixed inset-0 z-[300] flex flex-col items-center justify-center gap-4\"\n :class=\"opaque ? 'bg-surface' : 'bg-surface/80 backdrop-blur-sm'\"\n >\n <MSpinner :size=\"spinnerSize\" class=\"text-primary\" />\n <p v-if=\"text\" class=\"text-body-large text-on-surface-variant\">{{ text }}</p>\n <slot />\n </div>\n </Transition>\n </Teleport>\n\n <div v-else class=\"relative\">\n <slot name=\"content\" />\n <Transition\n enter-active-class=\"transition-opacity duration-200\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-150\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible\"\n class=\"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 rounded-[inherit]\"\n :class=\"opaque ? 'bg-surface' : 'bg-surface/80 backdrop-blur-sm'\"\n >\n <MSpinner :size=\"spinnerSize\" class=\"text-primary\" />\n <p v-if=\"text\" class=\"text-body-medium text-on-surface-variant\">{{ text }}</p>\n <slot />\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onBeforeUnmount, watch, nextTick, useSlots } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n cols?: number\n smCols?: number\n mdCols?: number\n lgCols?: number\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n }>(),\n { cols: 2, gap: 'md' },\n)\n\nconst gapPx: Record<string, number> = {\n none: 0,\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n}\n\nconst containerRef = ref<HTMLElement | null>(null)\nconst activeCols = ref(props.cols)\n\nfunction getActiveCols() {\n const w = window.innerWidth\n if (props.lgCols && w >= 1024) return props.lgCols\n if (props.mdCols && w >= 768) return props.mdCols\n if (props.smCols && w >= 640) return props.smCols\n return props.cols\n}\n\nconst slots = useSlots()\n\nfunction layout() {\n activeCols.value = getActiveCols()\n const container = containerRef.value\n if (!container) return\n\n const gap = gapPx[props.gap] ?? 0\n const cols = activeCols.value\n const children = Array.from(container.children) as HTMLElement[]\n\n const colWidth = (container.clientWidth - gap * (cols - 1)) / cols\n const colHeights = new Array<number>(cols).fill(0)\n\n for (const child of children) {\n const shortest = colHeights.indexOf(Math.min(...colHeights))\n const x = shortest * (colWidth + gap)\n const y = colHeights[shortest] ?? 0\n\n child.style.position = 'absolute'\n child.style.left = `${x}px`\n child.style.top = `${y}px`\n child.style.width = `${colWidth}px`\n\n colHeights[shortest] = (colHeights[shortest] ?? 0) + child.offsetHeight + gap\n }\n\n container.style.height = `${Math.max(...colHeights) - gap}px`\n}\n\nlet resizeObserver: ResizeObserver | null = null\n\nonMounted(() => {\n nextTick(layout)\n resizeObserver = new ResizeObserver(layout)\n if (containerRef.value) resizeObserver.observe(containerRef.value)\n window.addEventListener('resize', layout)\n})\n\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n window.removeEventListener('resize', layout)\n})\n\nwatch(() => [props.cols, props.smCols, props.mdCols, props.lgCols, props.gap], () => nextTick(layout))\nwatch(() => slots.default?.(), () => nextTick(layout), { flush: 'post' })\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"relative w-full\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onBeforeUnmount, watch, nextTick, useSlots } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n cols?: number\n smCols?: number\n mdCols?: number\n lgCols?: number\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n }>(),\n { cols: 2, gap: 'md' },\n)\n\nconst gapPx: Record<string, number> = {\n none: 0,\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n}\n\nconst containerRef = ref<HTMLElement | null>(null)\nconst activeCols = ref(props.cols)\n\nfunction getActiveCols() {\n const w = window.innerWidth\n if (props.lgCols && w >= 1024) return props.lgCols\n if (props.mdCols && w >= 768) return props.mdCols\n if (props.smCols && w >= 640) return props.smCols\n return props.cols\n}\n\nconst slots = useSlots()\n\nfunction layout() {\n activeCols.value = getActiveCols()\n const container = containerRef.value\n if (!container) return\n\n const gap = gapPx[props.gap] ?? 0\n const cols = activeCols.value\n const children = Array.from(container.children) as HTMLElement[]\n\n const colWidth = (container.clientWidth - gap * (cols - 1)) / cols\n const colHeights = new Array<number>(cols).fill(0)\n\n for (const child of children) {\n const shortest = colHeights.indexOf(Math.min(...colHeights))\n const x = shortest * (colWidth + gap)\n const y = colHeights[shortest] ?? 0\n\n child.style.position = 'absolute'\n child.style.left = `${x}px`\n child.style.top = `${y}px`\n child.style.width = `${colWidth}px`\n\n colHeights[shortest] = (colHeights[shortest] ?? 0) + child.offsetHeight + gap\n }\n\n container.style.height = `${Math.max(...colHeights) - gap}px`\n}\n\nlet resizeObserver: ResizeObserver | null = null\n\nonMounted(() => {\n nextTick(layout)\n resizeObserver = new ResizeObserver(layout)\n if (containerRef.value) resizeObserver.observe(containerRef.value)\n window.addEventListener('resize', layout)\n})\n\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n window.removeEventListener('resize', layout)\n})\n\nwatch(() => [props.cols, props.smCols, props.mdCols, props.lgCols, props.gap], () => nextTick(layout))\nwatch(() => slots.default?.(), () => nextTick(layout), { flush: 'post' })\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"relative w-full\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n /** Which edge of the trigger the dropdown aligns to. */\n align?: 'left' | 'right'\n }>(),\n { align: 'right' },\n)\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst dropStyle = ref<Record<string, string>>({})\n\nfunction computePos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const openAbove = spaceBelow < 200 && rect.top > spaceBelow\n\n const style: Record<string, string> = {\n maxHeight: `${Math.min(openAbove ? rect.top - 12 : spaceBelow, 400)}px`,\n }\n\n if (openAbove) {\n style.bottom = `${window.innerHeight - rect.top + 4}px`\n } else {\n style.top = `${rect.bottom + 4}px`\n }\n\n if (props.align === 'right') {\n style.right = `${window.innerWidth - rect.right}px`\n } else {\n style.left = `${rect.left}px`\n }\n\n dropStyle.value = style\n}\n\nfunction toggle() {\n if (!open.value) computePos()\n open.value = !open.value\n}\n\nfunction close() {\n open.value = false\n}\n\ndefineExpose({ close, open })\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!triggerEl.value?.contains(t) && !dropdownEl.value?.contains(t)) close()\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (dropdownEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { close(); return }\n computePos()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') close()\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n document.addEventListener('keydown', onKeydown)\n window.addEventListener('scroll', onScroll, true)\n})\n\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n document.removeEventListener('keydown', onKeydown)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst origin = computed(() =>\n props.align === 'right' ? 'top right' : 'top left',\n)\n</script>\n\n<template>\n <div ref=\"triggerEl\" class=\"inline-block\" @click=\"toggle\">\n <slot name=\"trigger\" :open=\"open\" />\n </div>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-100 ease-out\"\n enter-from-class=\"opacity-0 scale-95\"\n enter-to-class=\"opacity-100 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-75 ease-in\"\n leave-from-class=\"opacity-100 scale-100\"\n leave-to-class=\"opacity-0 scale-95\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] min-w-48 overflow-y-auto overflow-x-hidden rounded-xs bg-surface-container py-1 shadow-elevation-2\"\n :style=\"{ ...dropStyle, transformOrigin: origin }\"\n @click=\"close\"\n >\n <slot />\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n /** Which edge of the trigger the dropdown aligns to. */\n align?: 'left' | 'right'\n }>(),\n { align: 'right' },\n)\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst dropStyle = ref<Record<string, string>>({})\n\nfunction computePos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const openAbove = spaceBelow < 200 && rect.top > spaceBelow\n\n const style: Record<string, string> = {\n maxHeight: `${Math.min(openAbove ? rect.top - 12 : spaceBelow, 400)}px`,\n }\n\n if (openAbove) {\n style.bottom = `${window.innerHeight - rect.top + 4}px`\n } else {\n style.top = `${rect.bottom + 4}px`\n }\n\n if (props.align === 'right') {\n style.right = `${window.innerWidth - rect.right}px`\n } else {\n style.left = `${rect.left}px`\n }\n\n dropStyle.value = style\n}\n\nfunction toggle() {\n if (!open.value) computePos()\n open.value = !open.value\n}\n\nfunction close() {\n open.value = false\n}\n\ndefineExpose({ close, open })\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!triggerEl.value?.contains(t) && !dropdownEl.value?.contains(t)) close()\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (dropdownEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { close(); return }\n computePos()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') close()\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n document.addEventListener('keydown', onKeydown)\n window.addEventListener('scroll', onScroll, true)\n})\n\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n document.removeEventListener('keydown', onKeydown)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst origin = computed(() =>\n props.align === 'right' ? 'top right' : 'top left',\n)\n</script>\n\n<template>\n <div ref=\"triggerEl\" class=\"inline-block\" @click=\"toggle\">\n <slot name=\"trigger\" :open=\"open\" />\n </div>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-100 ease-out\"\n enter-from-class=\"opacity-0 scale-95\"\n enter-to-class=\"opacity-100 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-75 ease-in\"\n leave-from-class=\"opacity-100 scale-100\"\n leave-to-class=\"opacity-0 scale-95\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] min-w-48 overflow-y-auto overflow-x-hidden rounded-xs bg-surface-container py-1 shadow-elevation-2\"\n :style=\"{ ...dropStyle, transformOrigin: origin }\"\n @click=\"close\"\n >\n <slot />\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{ icon?: string }>(), {})\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left text-body-large text-on-surface hover:bg-on-surface/8\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"20\" class=\"text-on-surface-variant\" />\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{ icon?: string }>(), {})\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left text-body-large text-on-surface hover:bg-on-surface/8\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"20\" class=\"text-on-surface-variant\" />\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, onMounted, onUnmounted, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MCheckbox from './MCheckbox.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nexport interface MultiSelectOption {\n label: string\n value: string | number\n disabled?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: (string | number)[]\n options: MultiSelectOption[]\n label?: string\n placeholder?: string\n variant?: 'filled' | 'outlined'\n disabled?: boolean\n error?: string\n hint?: string\n required?: boolean\n leadingIcon?: string\n fieldBg?: string\n searchable?: boolean\n maxChips?: number\n }>(),\n {\n modelValue: () => [],\n variant: 'filled',\n disabled: false,\n required: false,\n searchable: true,\n maxChips: 3,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [(string | number)[]] }>()\n\nconst id = useId()\nconst open = ref(false)\nconst search = ref('')\nconst fieldEl = ref<HTMLElement | null>(null)\nconst { resolvedFieldBg } = useFieldBg(fieldEl, () => props.fieldBg)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst searchInput = ref<HTMLInputElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px', width: '0px' })\n\nconst hasValue = computed(() => props.modelValue.length > 0)\n\nconst filteredOptions = computed(() => {\n if (!search.value) return props.options\n const q = search.value.toLowerCase()\n return props.options.filter((o) => o.label.toLowerCase().includes(q))\n})\n\nconst visibleChips = computed(() =>\n props.modelValue.slice(0, props.maxChips).map((v) => ({\n value: v,\n label: props.options.find((o) => o.value === v)?.label ?? String(v),\n })),\n)\n\nconst overflowCount = computed(() => Math.max(0, props.modelValue.length - props.maxChips))\n\nfunction toggle(value: string | number) {\n const current = props.modelValue\n if (current.includes(value)) {\n emit('update:modelValue', current.filter((v) => v !== value))\n } else {\n emit('update:modelValue', [...current, value])\n }\n}\n\nfunction removeChip(value: string | number, e: Event) {\n e.stopPropagation()\n emit('update:modelValue', props.modelValue.filter((v) => v !== value))\n}\n\nfunction computeDropPos() {\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n dropPos.value = {\n top: `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n }\n}\n\nasync function openDropdown() {\n if (props.disabled) return\n computeDropPos()\n open.value = true\n search.value = ''\n await nextTick()\n searchInput.value?.focus()\n}\n\nfunction close() {\n open.value = false\n search.value = ''\n}\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!fieldEl.value?.contains(t) && !dropdownEl.value?.contains(t)) close()\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (dropdownEl.value?.contains(e.target as Node)) return\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) {\n close()\n return\n }\n computeDropPos()\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst triggerClasses = computed(() => {\n const base = [\n 'flex min-h-[56px] w-full cursor-pointer items-center gap-1.5 flex-wrap',\n 'transition-[border-color,border-width] duration-150',\n props.leadingIcon ? 'pl-12 pr-10' : 'pl-4 pr-10',\n ]\n\n if (props.variant === 'outlined') {\n return [\n ...base,\n 'rounded-sm border bg-transparent py-2',\n open.value\n ? (props.error ? 'border-2 border-error' : 'border-2 border-primary')\n : (props.error ? 'border-error' : 'border-outline hover:border-on-surface'),\n ].join(' ')\n }\n\n return [\n ...base,\n 'rounded-t-sm bg-surface-container-highest border-b pb-2',\n hasValue.value || open.value ? 'pt-7' : 'pt-4',\n open.value\n ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')\n : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),\n ].join(' ')\n})\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? (props.variant === 'outlined' ? 'left-11' : 'left-12')\n : (props.variant === 'outlined' ? 'left-3' : 'left-4')\n\n const floated = props.variant === 'outlined'\n ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'\n : 'top-2 translate-y-0 text-label-small'\n\n const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'\n const active = open.value || hasValue.value\n\n return [\n 'pointer-events-none absolute right-10 truncate transition-all duration-200',\n left,\n active ? floated : unFloated,\n open.value\n ? (props.error ? 'text-error' : 'text-primary')\n : (props.error ? 'text-error' : 'text-on-surface-variant'),\n ].join(' ')\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div\n ref=\"fieldEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <!-- Trigger field -->\n <div\n :id=\"id\"\n :class=\"triggerClasses\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n :aria-expanded=\"open\"\n :aria-haspopup=\"true\"\n @click=\"open ? close() : openDropdown()\"\n @keydown.enter.prevent=\"open ? close() : openDropdown()\"\n @keydown.space.prevent=\"open ? close() : openDropdown()\"\n @keydown.escape=\"close()\"\n >\n <template v-if=\"hasValue\">\n <span\n v-for=\"chip in visibleChips\"\n :key=\"chip.value\"\n class=\"inline-flex items-center gap-1 rounded-full bg-secondary-container px-2 py-0.5 text-label-small text-on-secondary-container\"\n >\n {{ chip.label }}\n <button\n type=\"button\"\n class=\"flex h-4 w-4 items-center justify-center rounded-full hover:bg-on-secondary-container/20\"\n @click=\"removeChip(chip.value, $event)\"\n >\n <MIcon name=\"close\" :size=\"12\" />\n </button>\n </span>\n <span\n v-if=\"overflowCount > 0\"\n class=\"rounded-full bg-surface-container-high px-2 py-0.5 text-label-small text-on-surface-variant\"\n >\n +{{ overflowCount }}\n </span>\n </template>\n <span v-else-if=\"!open\" class=\"text-body-large text-on-surface-variant opacity-0\">\n {{ placeholder }}\n </span>\n </div>\n\n <label :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <div class=\"pointer-events-none absolute right-2 top-7 -translate-y-1/2\">\n <MIcon\n :name=\"open ? 'arrow_drop_up' : 'arrow_drop_down'\"\n :size=\"24\"\n class=\"text-on-surface-variant\"\n />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n\n <!-- Dropdown teleported to body to escape overflow clipping -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n enter-to-class=\"opacity-100 translate-y-0 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-from-class=\"opacity-100 translate-y-0 scale-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] max-h-60 overflow-auto rounded-sm bg-surface-container shadow-elevation-2\"\n :style=\"dropPos\"\n >\n <!-- Search -->\n <div v-if=\"searchable\" class=\"sticky top-0 bg-surface-container px-3 py-2\">\n <div class=\"flex items-center gap-2 rounded-full bg-surface-container-high px-3 py-1.5\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n ref=\"searchInput\"\n v-model=\"search\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\"\n />\n </div>\n </div>\n\n <div class=\"flex flex-col py-1\">\n <label\n v-for=\"opt in filteredOptions\"\n :key=\"opt.value\"\n class=\"flex cursor-pointer items-center gap-3 px-4 py-2 hover:bg-on-surface/8\"\n :class=\"opt.disabled ? 'cursor-not-allowed opacity-38' : ''\"\n >\n <MCheckbox\n :model-value=\"modelValue.includes(opt.value)\"\n :disabled=\"opt.disabled\"\n @update:model-value=\"!opt.disabled && toggle(opt.value)\"\n />\n <span class=\"text-body-large text-on-surface\">{{ opt.label }}</span>\n </label>\n <p\n v-if=\"filteredOptions.length === 0\"\n class=\"px-4 py-3 text-center text-body-small text-on-surface-variant\"\n >\n Sin resultados\n </p>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, onMounted, onUnmounted, nextTick } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MCheckbox from './MCheckbox.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nexport interface MultiSelectOption {\n label: string\n value: string | number\n disabled?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: (string | number)[]\n options: MultiSelectOption[]\n label?: string\n placeholder?: string\n variant?: 'filled' | 'outlined'\n disabled?: boolean\n error?: string\n hint?: string\n required?: boolean\n leadingIcon?: string\n fieldBg?: string\n searchable?: boolean\n maxChips?: number\n }>(),\n {\n modelValue: () => [],\n variant: 'filled',\n disabled: false,\n required: false,\n searchable: true,\n maxChips: 3,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [(string | number)[]] }>()\n\nconst id = useId()\nconst open = ref(false)\nconst search = ref('')\nconst fieldEl = ref<HTMLElement | null>(null)\nconst { resolvedFieldBg } = useFieldBg(fieldEl, () => props.fieldBg)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst searchInput = ref<HTMLInputElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px', width: '0px' })\n\nconst hasValue = computed(() => props.modelValue.length > 0)\n\nconst filteredOptions = computed(() => {\n if (!search.value) return props.options\n const q = search.value.toLowerCase()\n return props.options.filter((o) => o.label.toLowerCase().includes(q))\n})\n\nconst visibleChips = computed(() =>\n props.modelValue.slice(0, props.maxChips).map((v) => ({\n value: v,\n label: props.options.find((o) => o.value === v)?.label ?? String(v),\n })),\n)\n\nconst overflowCount = computed(() => Math.max(0, props.modelValue.length - props.maxChips))\n\nfunction toggle(value: string | number) {\n const current = props.modelValue\n if (current.includes(value)) {\n emit('update:modelValue', current.filter((v) => v !== value))\n } else {\n emit('update:modelValue', [...current, value])\n }\n}\n\nfunction removeChip(value: string | number, e: Event) {\n e.stopPropagation()\n emit('update:modelValue', props.modelValue.filter((v) => v !== value))\n}\n\nfunction computeDropPos() {\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n dropPos.value = {\n top: `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n }\n}\n\nasync function openDropdown() {\n if (props.disabled) return\n computeDropPos()\n open.value = true\n search.value = ''\n await nextTick()\n searchInput.value?.focus()\n}\n\nfunction close() {\n open.value = false\n search.value = ''\n}\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!fieldEl.value?.contains(t) && !dropdownEl.value?.contains(t)) close()\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (dropdownEl.value?.contains(e.target as Node)) return\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) {\n close()\n return\n }\n computeDropPos()\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst triggerClasses = computed(() => {\n const base = [\n 'flex min-h-[56px] w-full cursor-pointer items-center gap-1.5 flex-wrap',\n 'transition-[border-color,border-width] duration-150',\n props.leadingIcon ? 'pl-12 pr-10' : 'pl-4 pr-10',\n ]\n\n if (props.variant === 'outlined') {\n return [\n ...base,\n 'rounded-sm border bg-transparent py-2',\n open.value\n ? (props.error ? 'border-2 border-error' : 'border-2 border-primary')\n : (props.error ? 'border-error' : 'border-outline hover:border-on-surface'),\n ].join(' ')\n }\n\n return [\n ...base,\n 'rounded-t-sm bg-surface-container-highest border-b pb-2',\n hasValue.value || open.value ? 'pt-7' : 'pt-4',\n open.value\n ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')\n : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),\n ].join(' ')\n})\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? (props.variant === 'outlined' ? 'left-11' : 'left-12')\n : (props.variant === 'outlined' ? 'left-3' : 'left-4')\n\n const floated = props.variant === 'outlined'\n ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'\n : 'top-2 translate-y-0 text-label-small'\n\n const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'\n const active = open.value || hasValue.value\n\n return [\n 'pointer-events-none absolute right-10 truncate transition-all duration-200',\n left,\n active ? floated : unFloated,\n open.value\n ? (props.error ? 'text-error' : 'text-primary')\n : (props.error ? 'text-error' : 'text-on-surface-variant'),\n ].join(' ')\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div\n ref=\"fieldEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <!-- Trigger field -->\n <div\n :id=\"id\"\n :class=\"triggerClasses\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n :aria-expanded=\"open\"\n :aria-haspopup=\"true\"\n @click=\"open ? close() : openDropdown()\"\n @keydown.enter.prevent=\"open ? close() : openDropdown()\"\n @keydown.space.prevent=\"open ? close() : openDropdown()\"\n @keydown.escape=\"close()\"\n >\n <template v-if=\"hasValue\">\n <span\n v-for=\"chip in visibleChips\"\n :key=\"chip.value\"\n class=\"inline-flex items-center gap-1 rounded-full bg-secondary-container px-2 py-0.5 text-label-small text-on-secondary-container\"\n >\n {{ chip.label }}\n <button\n type=\"button\"\n class=\"flex h-4 w-4 items-center justify-center rounded-full hover:bg-on-secondary-container/20\"\n @click=\"removeChip(chip.value, $event)\"\n >\n <MIcon name=\"close\" :size=\"12\" />\n </button>\n </span>\n <span\n v-if=\"overflowCount > 0\"\n class=\"rounded-full bg-surface-container-high px-2 py-0.5 text-label-small text-on-surface-variant\"\n >\n +{{ overflowCount }}\n </span>\n </template>\n <span v-else-if=\"!open\" class=\"text-body-large text-on-surface-variant opacity-0\">\n {{ placeholder }}\n </span>\n </div>\n\n <label :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <div class=\"pointer-events-none absolute right-2 top-7 -translate-y-1/2\">\n <MIcon\n :name=\"open ? 'arrow_drop_up' : 'arrow_drop_down'\"\n :size=\"24\"\n class=\"text-on-surface-variant\"\n />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n\n <!-- Dropdown teleported to body to escape overflow clipping -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n enter-to-class=\"opacity-100 translate-y-0 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-from-class=\"opacity-100 translate-y-0 scale-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] max-h-60 overflow-auto rounded-sm bg-surface-container shadow-elevation-2\"\n :style=\"dropPos\"\n >\n <!-- Search -->\n <div v-if=\"searchable\" class=\"sticky top-0 bg-surface-container px-3 py-2\">\n <div class=\"flex items-center gap-2 rounded-full bg-surface-container-high px-3 py-1.5\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n ref=\"searchInput\"\n v-model=\"search\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\"\n />\n </div>\n </div>\n\n <div class=\"flex flex-col py-1\">\n <label\n v-for=\"opt in filteredOptions\"\n :key=\"opt.value\"\n class=\"flex cursor-pointer items-center gap-3 px-4 py-2 hover:bg-on-surface/8\"\n :class=\"opt.disabled ? 'cursor-not-allowed opacity-38' : ''\"\n >\n <MCheckbox\n :model-value=\"modelValue.includes(opt.value)\"\n :disabled=\"opt.disabled\"\n @update:model-value=\"!opt.disabled && toggle(opt.value)\"\n />\n <span class=\"text-body-large text-on-surface\">{{ opt.label }}</span>\n </label>\n <p\n v-if=\"filteredOptions.length === 0\"\n class=\"px-4 py-3 text-center text-body-small text-on-surface-variant\"\n >\n Sin resultados\n </p>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MBadge from './MBadge.vue'\n\nexport interface NavBarItem {\n value: string | number\n label: string\n icon: string\n badge?: number\n badgeDot?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number\n items: NavBarItem[]\n}>(), {})\n\ndefineEmits<{ 'update:modelValue': [string | number] }>()\n</script>\n\n<template>\n <nav class=\"flex h-20 w-full items-center justify-around border-t border-outline-variant bg-surface-container\">\n <button\n v-for=\"item in items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"group flex flex-1 cursor-pointer flex-col items-center justify-center gap-1 self-stretch transition-colors focus-visible:outline-none\"\n :class=\"\n item.value === modelValue\n ? 'text-on-secondary-container'\n : 'text-on-surface-variant'\n \"\n @click=\"$emit('update:modelValue', item.value)\"\n >\n <!-- Pill indicator with icon -->\n <span\n class=\"inline-flex h-8 items-center justify-center rounded-2xl transition-all duration-200\"\n :class=\"\n item.value === modelValue\n ? 'w-16 bg-secondary-container'\n : 'w-0 bg-secondary-container/0 group-hover:w-16 group-hover:bg-on-surface/8'\n \"\n >\n <MBadge v-if=\"item.badge != null\" :count=\"item.badge\">\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MBadge v-else-if=\"item.badgeDot\" dot>\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MIcon v-else :name=\"item.icon\" :size=\"24\" />\n </span>\n\n <!-- Label -->\n <span\n class=\"text-label-medium transition-[font-weight] duration-150\"\n :class=\"item.value === modelValue ? 'font-bold' : 'font-medium'\"\n >\n {{ item.label }}\n </span>\n </button>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MBadge from './MBadge.vue'\n\nexport interface NavBarItem {\n value: string | number\n label: string\n icon: string\n badge?: number\n badgeDot?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number\n items: NavBarItem[]\n}>(), {})\n\ndefineEmits<{ 'update:modelValue': [string | number] }>()\n</script>\n\n<template>\n <nav class=\"flex h-20 w-full items-center justify-around border-t border-outline-variant bg-surface-container\">\n <button\n v-for=\"item in items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"group flex flex-1 cursor-pointer flex-col items-center justify-center gap-1 self-stretch transition-colors focus-visible:outline-none\"\n :class=\"\n item.value === modelValue\n ? 'text-on-secondary-container'\n : 'text-on-surface-variant'\n \"\n @click=\"$emit('update:modelValue', item.value)\"\n >\n <!-- Pill indicator with icon -->\n <span\n class=\"inline-flex h-8 items-center justify-center rounded-2xl transition-all duration-200\"\n :class=\"\n item.value === modelValue\n ? 'w-16 bg-secondary-container'\n : 'w-0 bg-secondary-container/0 group-hover:w-16 group-hover:bg-on-surface/8'\n \"\n >\n <MBadge v-if=\"item.badge != null\" :count=\"item.badge\">\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MBadge v-else-if=\"item.badgeDot\" dot>\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MIcon v-else :name=\"item.icon\" :size=\"24\" />\n </span>\n\n <!-- Label -->\n <span\n class=\"text-label-medium transition-[font-weight] duration-150\"\n :class=\"item.value === modelValue ? 'font-bold' : 'font-medium'\"\n >\n {{ item.label }}\n </span>\n </button>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface DrawerItem {\n value: string | number\n label: string\n icon?: string\n badge?: string | number\n disabled?: boolean\n}\n\nexport interface DrawerSection {\n title?: string\n items: DrawerItem[]\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: boolean\n selected?: string | number\n sections: DrawerSection[]\n title?: string\n modal?: boolean\n}>(), { modal: true })\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n select: [string | number]\n}>()\n\nfunction close() { emit('update:modelValue', false) }\nfunction select(item: DrawerItem) {\n if (item.disabled) return\n emit('select', item.value)\n if (props.modal) close()\n}\n\nwatch(() => props.modelValue, (open) => {\n if (open) document.body.style.overflow = 'hidden'\n else document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <!-- Modal variant -->\n <Teleport v-if=\"modal\" to=\"body\">\n <Transition name=\"nd\" :duration=\"{ enter: 300, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[100] flex\">\n <!-- Scrim -->\n <div class=\"nd-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <nav class=\"nd-panel relative flex h-full w-72 max-w-[85vw] flex-col bg-surface-container shadow-elevation-3\">\n <!-- Header -->\n <div v-if=\"title || $slots.header\" class=\"shrink-0 px-5 pt-6 pb-2\">\n <slot name=\"header\">\n <h2 class=\"text-title-small font-medium text-on-surface-variant\">{{ title }}</h2>\n </slot>\n </div>\n\n <!-- Sections -->\n <div class=\"flex-1 overflow-y-auto px-3 py-2\">\n <template v-for=\"(section, si) in sections\" :key=\"si\">\n <div v-if=\"si > 0\" class=\"my-2 border-t border-outline-variant\" />\n <p v-if=\"section.title\" class=\"px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant\">\n {{ section.title }}\n </p>\n <button\n v-for=\"item in section.items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-[0.38]'\n : item.value === selected\n ? 'bg-secondary-container text-on-secondary-container'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',\n ]\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"24\" />\n <span class=\"flex-1 text-label-large font-medium\">{{ item.label }}</span>\n <span\n v-if=\"item.badge != null\"\n class=\"text-label-medium text-on-surface-variant\"\n >\n {{ item.badge }}\n </span>\n </button>\n </template>\n </div>\n </nav>\n </div>\n </Transition>\n </Teleport>\n\n <!-- Standard (inline) variant -->\n <nav\n v-else\n class=\"flex h-full w-72 flex-col border-r border-outline-variant bg-surface\"\n >\n <div v-if=\"title || $slots.header\" class=\"shrink-0 px-5 pt-6 pb-2\">\n <slot name=\"header\">\n <h2 class=\"text-title-small font-medium text-on-surface-variant\">{{ title }}</h2>\n </slot>\n </div>\n <div class=\"flex-1 overflow-y-auto px-3 py-2\">\n <template v-for=\"(section, si) in sections\" :key=\"si\">\n <div v-if=\"si > 0\" class=\"my-2 border-t border-outline-variant\" />\n <p v-if=\"section.title\" class=\"px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant\">\n {{ section.title }}\n </p>\n <button\n v-for=\"item in section.items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-[0.38]'\n : item.value === selected\n ? 'bg-secondary-container text-on-secondary-container'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',\n ]\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"24\" />\n <span class=\"flex-1 text-label-large font-medium\">{{ item.label }}</span>\n <span v-if=\"item.badge != null\" class=\"text-label-medium text-on-surface-variant\">\n {{ item.badge }}\n </span>\n </button>\n </template>\n </div>\n </nav>\n</template>\n\n<style scoped>\n.nd-scrim {\n transition: opacity 280ms ease;\n}\n.nd-enter-from .nd-scrim,\n.nd-leave-to .nd-scrim {\n opacity: 0;\n}\n\n.nd-panel {\n transition: transform 300ms cubic-bezier(0.2, 0, 0, 1);\n}\n.nd-enter-from .nd-panel,\n.nd-leave-to .nd-panel {\n transform: translateX(-100%);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface DrawerItem {\n value: string | number\n label: string\n icon?: string\n badge?: string | number\n disabled?: boolean\n}\n\nexport interface DrawerSection {\n title?: string\n items: DrawerItem[]\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: boolean\n selected?: string | number\n sections: DrawerSection[]\n title?: string\n modal?: boolean\n}>(), { modal: true })\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n select: [string | number]\n}>()\n\nfunction close() { emit('update:modelValue', false) }\nfunction select(item: DrawerItem) {\n if (item.disabled) return\n emit('select', item.value)\n if (props.modal) close()\n}\n\nwatch(() => props.modelValue, (open) => {\n if (open) document.body.style.overflow = 'hidden'\n else document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <!-- Modal variant -->\n <Teleport v-if=\"modal\" to=\"body\">\n <Transition name=\"nd\" :duration=\"{ enter: 300, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[100] flex\">\n <!-- Scrim -->\n <div class=\"nd-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <nav class=\"nd-panel relative flex h-full w-72 max-w-[85vw] flex-col bg-surface-container shadow-elevation-3\">\n <!-- Header -->\n <div v-if=\"title || $slots.header\" class=\"shrink-0 px-5 pt-6 pb-2\">\n <slot name=\"header\">\n <h2 class=\"text-title-small font-medium text-on-surface-variant\">{{ title }}</h2>\n </slot>\n </div>\n\n <!-- Sections -->\n <div class=\"flex-1 overflow-y-auto px-3 py-2\">\n <template v-for=\"(section, si) in sections\" :key=\"si\">\n <div v-if=\"si > 0\" class=\"my-2 border-t border-outline-variant\" />\n <p v-if=\"section.title\" class=\"px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant\">\n {{ section.title }}\n </p>\n <button\n v-for=\"item in section.items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-[0.38]'\n : item.value === selected\n ? 'bg-secondary-container text-on-secondary-container'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',\n ]\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"24\" />\n <span class=\"flex-1 text-label-large font-medium\">{{ item.label }}</span>\n <span\n v-if=\"item.badge != null\"\n class=\"text-label-medium text-on-surface-variant\"\n >\n {{ item.badge }}\n </span>\n </button>\n </template>\n </div>\n </nav>\n </div>\n </Transition>\n </Teleport>\n\n <!-- Standard (inline) variant -->\n <nav\n v-else\n class=\"flex h-full w-72 flex-col border-r border-outline-variant bg-surface\"\n >\n <div v-if=\"title || $slots.header\" class=\"shrink-0 px-5 pt-6 pb-2\">\n <slot name=\"header\">\n <h2 class=\"text-title-small font-medium text-on-surface-variant\">{{ title }}</h2>\n </slot>\n </div>\n <div class=\"flex-1 overflow-y-auto px-3 py-2\">\n <template v-for=\"(section, si) in sections\" :key=\"si\">\n <div v-if=\"si > 0\" class=\"my-2 border-t border-outline-variant\" />\n <p v-if=\"section.title\" class=\"px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant\">\n {{ section.title }}\n </p>\n <button\n v-for=\"item in section.items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none\"\n :class=\"[\n item.disabled\n ? 'cursor-not-allowed opacity-[0.38]'\n : item.value === selected\n ? 'bg-secondary-container text-on-secondary-container'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',\n ]\"\n :disabled=\"item.disabled\"\n @click=\"select(item)\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"24\" />\n <span class=\"flex-1 text-label-large font-medium\">{{ item.label }}</span>\n <span v-if=\"item.badge != null\" class=\"text-label-medium text-on-surface-variant\">\n {{ item.badge }}\n </span>\n </button>\n </template>\n </div>\n </nav>\n</template>\n\n<style scoped>\n.nd-scrim {\n transition: opacity 280ms ease;\n}\n.nd-enter-from .nd-scrim,\n.nd-leave-to .nd-scrim {\n opacity: 0;\n}\n\n.nd-panel {\n transition: transform 300ms cubic-bezier(0.2, 0, 0, 1);\n}\n.nd-enter-from .nd-panel,\n.nd-leave-to .nd-panel {\n transform: translateX(-100%);\n}\n</style>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MBadge from './MBadge.vue'\n\nexport interface NavRailItem {\n value: string | number\n label: string\n icon: string\n badge?: number\n badgeDot?: boolean\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number\n items: NavRailItem[]\n alignment?: 'top' | 'center' | 'bottom'\n}>(), { alignment: 'top' })\n\ndefineEmits<{ 'update:modelValue': [string | number] }>()\n</script>\n\n<template>\n <nav class=\"flex h-full w-20 flex-col items-center border-r border-outline-variant bg-surface\">\n <!-- FAB slot -->\n <div v-if=\"$slots.fab\" class=\"flex shrink-0 items-center justify-center pt-3 pb-2\">\n <slot name=\"fab\" />\n </div>\n\n <!-- Items -->\n <div\n class=\"flex flex-1 flex-col items-center gap-1 py-3\"\n :class=\"{\n 'justify-start': alignment === 'top',\n 'justify-center': alignment === 'center',\n 'justify-end': alignment === 'bottom',\n }\"\n >\n <button\n v-for=\"item in items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"group flex w-full cursor-pointer flex-col items-center justify-center gap-1 px-3 py-2 focus-visible:outline-none\"\n :class=\"item.disabled ? 'cursor-not-allowed opacity-[0.38]' : ''\"\n :disabled=\"item.disabled\"\n @click=\"!item.disabled && $emit('update:modelValue', item.value)\"\n >\n <!-- Pill indicator -->\n <span\n class=\"inline-flex h-8 items-center justify-center rounded-2xl transition-all duration-200\"\n :class=\"\n item.value === modelValue\n ? 'w-14 bg-secondary-container text-on-secondary-container'\n : 'w-14 bg-transparent text-on-surface-variant group-hover:bg-on-surface/8'\n \"\n >\n <MBadge v-if=\"item.badge != null\" :count=\"item.badge\">\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MBadge v-else-if=\"item.badgeDot\" dot>\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MIcon v-else :name=\"item.icon\" :size=\"24\" />\n </span>\n\n <!-- Label -->\n <span\n class=\"max-w-[56px] truncate text-center text-label-medium\"\n :class=\"\n item.value === modelValue\n ? 'font-bold text-on-surface'\n : 'font-medium text-on-surface-variant'\n \"\n >\n {{ item.label }}\n </span>\n </button>\n </div>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MBadge from './MBadge.vue'\n\nexport interface NavRailItem {\n value: string | number\n label: string\n icon: string\n badge?: number\n badgeDot?: boolean\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number\n items: NavRailItem[]\n alignment?: 'top' | 'center' | 'bottom'\n}>(), { alignment: 'top' })\n\ndefineEmits<{ 'update:modelValue': [string | number] }>()\n</script>\n\n<template>\n <nav class=\"flex h-full w-20 flex-col items-center border-r border-outline-variant bg-surface\">\n <!-- FAB slot -->\n <div v-if=\"$slots.fab\" class=\"flex shrink-0 items-center justify-center pt-3 pb-2\">\n <slot name=\"fab\" />\n </div>\n\n <!-- Items -->\n <div\n class=\"flex flex-1 flex-col items-center gap-1 py-3\"\n :class=\"{\n 'justify-start': alignment === 'top',\n 'justify-center': alignment === 'center',\n 'justify-end': alignment === 'bottom',\n }\"\n >\n <button\n v-for=\"item in items\"\n :key=\"item.value\"\n type=\"button\"\n class=\"group flex w-full cursor-pointer flex-col items-center justify-center gap-1 px-3 py-2 focus-visible:outline-none\"\n :class=\"item.disabled ? 'cursor-not-allowed opacity-[0.38]' : ''\"\n :disabled=\"item.disabled\"\n @click=\"!item.disabled && $emit('update:modelValue', item.value)\"\n >\n <!-- Pill indicator -->\n <span\n class=\"inline-flex h-8 items-center justify-center rounded-2xl transition-all duration-200\"\n :class=\"\n item.value === modelValue\n ? 'w-14 bg-secondary-container text-on-secondary-container'\n : 'w-14 bg-transparent text-on-surface-variant group-hover:bg-on-surface/8'\n \"\n >\n <MBadge v-if=\"item.badge != null\" :count=\"item.badge\">\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MBadge v-else-if=\"item.badgeDot\" dot>\n <MIcon :name=\"item.icon\" :size=\"24\" />\n </MBadge>\n <MIcon v-else :name=\"item.icon\" :size=\"24\" />\n </span>\n\n <!-- Label -->\n <span\n class=\"max-w-[56px] truncate text-center text-label-medium\"\n :class=\"\n item.value === modelValue\n ? 'font-bold text-on-surface'\n : 'font-medium text-on-surface-variant'\n \"\n >\n {{ item.label }}\n </span>\n </button>\n </div>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n value?: number;\n indeterminate?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n variant?: \"linear\" | \"wavy\";\n label?: string;\n }>(),\n {\n color: \"primary\",\n variant: \"linear\",\n },\n);\n\nconst isIndeterminate = computed(() => props.indeterminate || props.value === undefined);\nconst clampedValue = computed(() => Math.min(100, Math.max(0, props.value ?? 0)));\n\nconst colorMap: Record<\n \"primary\" | \"secondary\" | \"tertiary\" | \"error\",\n { bar: string; track: string; text: string }\n> = {\n primary: { bar: \"bg-primary\", track: \"bg-primary-container\", text: \"text-primary\" },\n secondary: { bar: \"bg-secondary\", track: \"bg-secondary-container\", text: \"text-secondary\" },\n tertiary: { bar: \"bg-tertiary\", track: \"bg-tertiary-container\", text: \"text-tertiary\" },\n error: { bar: \"bg-error\", track: \"bg-error-container\", text: \"text-error\" },\n};\n\n// ── Wave geometry ────────────────────────────────────────────────────────\n// Smooth sine wave sampled as a single polyline path.\n// Period = 20px (one full up-down cycle). We render a wide strip so that\n// translating by exactly one period gives a seamless infinite scroll.\nconst PERIOD = 20; // px per full sine cycle\nconst AMP = 2.5; // amplitude (bar is 8px tall, mid at 4)\nconst MID = 4;\nconst VIEW_H = 8;\nconst PERIODS = 80; // total cycles → 1600px strip\nconst STEP = 1; // px sampling resolution\n\nconst waveWidth = PERIOD * PERIODS;\n\nconst wavePath = (() => {\n let d = \"\";\n for (let x = 0; x <= waveWidth; x += STEP) {\n const y = MID - AMP * Math.sin((x / PERIOD) * Math.PI * 2);\n d += (x === 0 ? \"M\" : \"L\") + x + \",\" + y.toFixed(2) + \" \";\n }\n return d.trim();\n})();\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <span v-if=\"label\" class=\"text-label-small text-on-surface-variant\">{{ label }}</span>\n\n <!-- ── Linear variant ────────────────────────────────────────────────── -->\n <div\n v-if=\"variant === 'linear'\"\n class=\"relative h-1 w-full overflow-hidden rounded-full\"\n :class=\"colorMap[color].track\"\n role=\"progressbar\"\n :aria-valuenow=\"isIndeterminate ? undefined : clampedValue\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <div\n v-if=\"!isIndeterminate\"\n class=\"h-full rounded-full transition-[width] duration-300 ease-in-out\"\n :class=\"colorMap[color].bar\"\n :style=\"{ width: `${clampedValue}%` }\"\n />\n <div\n v-else\n class=\"absolute inset-y-0 w-2/5 rounded-full animate-[m3-progress-indeterminate_1.6s_ease-in-out_infinite]\"\n :class=\"colorMap[color].bar\"\n />\n </div>\n\n <!-- ── Wavy variant ───────────────────────────────────────────────────── -->\n <div\n v-else\n class=\"relative h-2 w-full overflow-visible\"\n role=\"progressbar\"\n :aria-valuenow=\"isIndeterminate ? undefined : clampedValue\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <!-- DETERMINATE -->\n <template v-if=\"!isIndeterminate\">\n <!-- Active (wavy) portion: clipped to value%, but the wave keeps flowing -->\n <div\n class=\"absolute inset-0 overflow-hidden\"\n :style=\"{\n clipPath: `inset(0 ${100 - clampedValue}% 0 0)`,\n transition: 'clip-path 300ms ease',\n }\"\n >\n <div\n class=\"absolute top-0 left-0 h-full animate-[m3-wave-flow_0.8s_linear_infinite]\"\n :class=\"colorMap[color].text\"\n :style=\"{ width: `${waveWidth}px` }\"\n >\n <svg\n :width=\"waveWidth\"\n :height=\"VIEW_H\"\n :viewBox=\"`0 0 ${waveWidth} ${VIEW_H}`\"\n class=\"h-full\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n :d=\"wavePath\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Inactive (straight track) portion -->\n <div\n class=\"absolute inset-y-0 right-0 flex items-center\"\n :class=\"colorMap[color].track\"\n :style=\"{ left: `calc(${clampedValue}% + 4px)`, transition: 'left 300ms ease' }\"\n style=\"border-radius: 9999px; height: 4px; top: 50%; transform: translateY(-50%)\"\n />\n\n <!-- Stop indicator (dot at the end of the track) -->\n <div\n class=\"absolute rounded-full\"\n :class=\"colorMap[color].bar\"\n :style=\"{\n right: '0',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '4px',\n height: '4px',\n }\"\n />\n </template>\n\n <!-- INDETERMINATE -->\n <div v-else class=\"absolute inset-0 overflow-hidden rounded-full\">\n <div\n class=\"absolute top-0 left-0 h-full animate-[m3-wave-flow_0.9s_linear_infinite]\"\n :class=\"colorMap[color].text\"\n :style=\"{ width: `${waveWidth}px` }\"\n >\n <svg\n :width=\"waveWidth\"\n :height=\"VIEW_H\"\n :viewBox=\"`0 0 ${waveWidth} ${VIEW_H}`\"\n class=\"h-full\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n :d=\"wavePath\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n/* Scroll exactly one period (20px) so the loop is perfectly seamless. */\n@keyframes m3-wave-flow {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-20px);\n }\n}\n\n@keyframes m3-progress-indeterminate {\n 0% {\n left: -40%;\n }\n 100% {\n left: 100%;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .animate-\\[m3-wave-flow_1\\.2s_linear_infinite\\],\n .animate-\\[m3-wave-flow_0\\.9s_linear_infinite\\],\n .animate-\\[m3-progress-indeterminate_1\\.6s_ease-in-out_infinite\\] {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n value?: number;\n indeterminate?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n variant?: \"linear\" | \"wavy\";\n label?: string;\n }>(),\n {\n color: \"primary\",\n variant: \"linear\",\n },\n);\n\nconst isIndeterminate = computed(() => props.indeterminate || props.value === undefined);\nconst clampedValue = computed(() => Math.min(100, Math.max(0, props.value ?? 0)));\n\nconst colorMap: Record<\n \"primary\" | \"secondary\" | \"tertiary\" | \"error\",\n { bar: string; track: string; text: string }\n> = {\n primary: { bar: \"bg-primary\", track: \"bg-primary-container\", text: \"text-primary\" },\n secondary: { bar: \"bg-secondary\", track: \"bg-secondary-container\", text: \"text-secondary\" },\n tertiary: { bar: \"bg-tertiary\", track: \"bg-tertiary-container\", text: \"text-tertiary\" },\n error: { bar: \"bg-error\", track: \"bg-error-container\", text: \"text-error\" },\n};\n\n// ── Wave geometry ────────────────────────────────────────────────────────\n// Smooth sine wave sampled as a single polyline path.\n// Period = 20px (one full up-down cycle). We render a wide strip so that\n// translating by exactly one period gives a seamless infinite scroll.\nconst PERIOD = 20; // px per full sine cycle\nconst AMP = 2.5; // amplitude (bar is 8px tall, mid at 4)\nconst MID = 4;\nconst VIEW_H = 8;\nconst PERIODS = 80; // total cycles → 1600px strip\nconst STEP = 1; // px sampling resolution\n\nconst waveWidth = PERIOD * PERIODS;\n\nconst wavePath = (() => {\n let d = \"\";\n for (let x = 0; x <= waveWidth; x += STEP) {\n const y = MID - AMP * Math.sin((x / PERIOD) * Math.PI * 2);\n d += (x === 0 ? \"M\" : \"L\") + x + \",\" + y.toFixed(2) + \" \";\n }\n return d.trim();\n})();\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <span v-if=\"label\" class=\"text-label-small text-on-surface-variant\">{{ label }}</span>\n\n <!-- ── Linear variant ────────────────────────────────────────────────── -->\n <div\n v-if=\"variant === 'linear'\"\n class=\"relative h-1 w-full overflow-hidden rounded-full\"\n :class=\"colorMap[color].track\"\n role=\"progressbar\"\n :aria-valuenow=\"isIndeterminate ? undefined : clampedValue\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <div\n v-if=\"!isIndeterminate\"\n class=\"h-full rounded-full transition-[width] duration-300 ease-in-out\"\n :class=\"colorMap[color].bar\"\n :style=\"{ width: `${clampedValue}%` }\"\n />\n <div\n v-else\n class=\"absolute inset-y-0 w-2/5 rounded-full animate-[m3-progress-indeterminate_1.6s_ease-in-out_infinite]\"\n :class=\"colorMap[color].bar\"\n />\n </div>\n\n <!-- ── Wavy variant ───────────────────────────────────────────────────── -->\n <div\n v-else\n class=\"relative h-2 w-full overflow-visible\"\n role=\"progressbar\"\n :aria-valuenow=\"isIndeterminate ? undefined : clampedValue\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <!-- DETERMINATE -->\n <template v-if=\"!isIndeterminate\">\n <!-- Active (wavy) portion: clipped to value%, but the wave keeps flowing -->\n <div\n class=\"absolute inset-0 overflow-hidden\"\n :style=\"{\n clipPath: `inset(0 ${100 - clampedValue}% 0 0)`,\n transition: 'clip-path 300ms ease',\n }\"\n >\n <div\n class=\"absolute top-0 left-0 h-full animate-[m3-wave-flow_0.8s_linear_infinite]\"\n :class=\"colorMap[color].text\"\n :style=\"{ width: `${waveWidth}px` }\"\n >\n <svg\n :width=\"waveWidth\"\n :height=\"VIEW_H\"\n :viewBox=\"`0 0 ${waveWidth} ${VIEW_H}`\"\n class=\"h-full\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n :d=\"wavePath\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Inactive (straight track) portion -->\n <div\n class=\"absolute inset-y-0 right-0 flex items-center\"\n :class=\"colorMap[color].track\"\n :style=\"{ left: `calc(${clampedValue}% + 4px)`, transition: 'left 300ms ease' }\"\n style=\"border-radius: 9999px; height: 4px; top: 50%; transform: translateY(-50%)\"\n />\n\n <!-- Stop indicator (dot at the end of the track) -->\n <div\n class=\"absolute rounded-full\"\n :class=\"colorMap[color].bar\"\n :style=\"{\n right: '0',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '4px',\n height: '4px',\n }\"\n />\n </template>\n\n <!-- INDETERMINATE -->\n <div v-else class=\"absolute inset-0 overflow-hidden rounded-full\">\n <div\n class=\"absolute top-0 left-0 h-full animate-[m3-wave-flow_0.9s_linear_infinite]\"\n :class=\"colorMap[color].text\"\n :style=\"{ width: `${waveWidth}px` }\"\n >\n <svg\n :width=\"waveWidth\"\n :height=\"VIEW_H\"\n :viewBox=\"`0 0 ${waveWidth} ${VIEW_H}`\"\n class=\"h-full\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n :d=\"wavePath\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n/* Scroll exactly one period (20px) so the loop is perfectly seamless. */\n@keyframes m3-wave-flow {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-20px);\n }\n}\n\n@keyframes m3-progress-indeterminate {\n 0% {\n left: -40%;\n }\n 100% {\n left: 100%;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .animate-\\[m3-wave-flow_1\\.2s_linear_infinite\\],\n .animate-\\[m3-wave-flow_0\\.9s_linear_infinite\\],\n .animate-\\[m3-progress-indeterminate_1\\.6s_ease-in-out_infinite\\] {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: unknown;\n value: unknown;\n label?: string;\n disabled?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n }>(),\n { disabled: false, color: \"primary\" },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [unknown] }>();\nconst id = useId();\nconst isChecked = computed(() => props.modelValue === props.value);\n\n// Ring + dot color when checked, applied via currentColor on the SVG.\nconst checkedColor: Record<string, string> = {\n primary: \"text-primary\",\n secondary: \"text-secondary\",\n tertiary: \"text-tertiary\",\n error: \"text-error\",\n};\n</script>\n\n<template>\n <label\n :for=\"id\"\n class=\"inline-flex items-center gap-3 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span class=\"relative flex h-5 w-5 shrink-0\">\n <input\n :id=\"id\"\n type=\"radio\"\n class=\"sr-only\"\n :checked=\"isChecked\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', value)\"\n />\n\n <!--\n SVG radio: vector circles sharing center (10,10) stay round + concentric\n at any zoom. Outer ring uses r=8 (not 9) so the 2px stroke (7..9) leaves\n ~1px of clearance to the viewBox edge — prevents the border getting\n clipped at certain zoom levels.\n -->\n <svg\n viewBox=\"0 0 20 20\"\n class=\"h-full w-full transition-colors duration-150\"\n :class=\"isChecked ? checkedColor[color] : 'text-on-surface-variant'\"\n aria-hidden=\"true\"\n >\n <circle cx=\"10\" cy=\"10\" r=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n <!--\n Dot scaled via CSS, but the transform-origin is pinned to the circle's\n own bounding box center (transform-box: fill-box). Without this, the SVG\n element origin is (0,0) of the viewBox, so scale() grows from a corner\n and the dot visibly slides to the center. fill-box fixes the origin to\n the dot itself, so it grows symmetrically in place.\n -->\n <circle\n class=\"m3-radio-dot\"\n :class=\"{ 'is-checked': isChecked }\"\n cx=\"10\"\n cy=\"10\"\n r=\"4.5\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n\n <span v-if=\"label\" class=\"text-body-large text-on-surface\">{{ label }}</span>\n </label>\n</template>\n\n<style scoped>\n.m3-radio-dot {\n transform: scale(0);\n transform-box: fill-box;\n transform-origin: center;\n transition: transform 150ms ease;\n}\n.m3-radio-dot.is-checked {\n transform: scale(1);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: unknown;\n value: unknown;\n label?: string;\n disabled?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n }>(),\n { disabled: false, color: \"primary\" },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [unknown] }>();\nconst id = useId();\nconst isChecked = computed(() => props.modelValue === props.value);\n\n// Ring + dot color when checked, applied via currentColor on the SVG.\nconst checkedColor: Record<string, string> = {\n primary: \"text-primary\",\n secondary: \"text-secondary\",\n tertiary: \"text-tertiary\",\n error: \"text-error\",\n};\n</script>\n\n<template>\n <label\n :for=\"id\"\n class=\"inline-flex items-center gap-3 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span class=\"relative flex h-5 w-5 shrink-0\">\n <input\n :id=\"id\"\n type=\"radio\"\n class=\"sr-only\"\n :checked=\"isChecked\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', value)\"\n />\n\n <!--\n SVG radio: vector circles sharing center (10,10) stay round + concentric\n at any zoom. Outer ring uses r=8 (not 9) so the 2px stroke (7..9) leaves\n ~1px of clearance to the viewBox edge — prevents the border getting\n clipped at certain zoom levels.\n -->\n <svg\n viewBox=\"0 0 20 20\"\n class=\"h-full w-full transition-colors duration-150\"\n :class=\"isChecked ? checkedColor[color] : 'text-on-surface-variant'\"\n aria-hidden=\"true\"\n >\n <circle cx=\"10\" cy=\"10\" r=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n <!--\n Dot scaled via CSS, but the transform-origin is pinned to the circle's\n own bounding box center (transform-box: fill-box). Without this, the SVG\n element origin is (0,0) of the viewBox, so scale() grows from a corner\n and the dot visibly slides to the center. fill-box fixes the origin to\n the dot itself, so it grows symmetrically in place.\n -->\n <circle\n class=\"m3-radio-dot\"\n :class=\"{ 'is-checked': isChecked }\"\n cx=\"10\"\n cy=\"10\"\n r=\"4.5\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n\n <span v-if=\"label\" class=\"text-body-large text-on-surface\">{{ label }}</span>\n </label>\n</template>\n\n<style scoped>\n.m3-radio-dot {\n transform: scale(0);\n transform-box: fill-box;\n transform-origin: center;\n transition: transform 150ms ease;\n}\n.m3-radio-dot.is-checked {\n transform: scale(1);\n}\n</style>\n","<script setup lang=\"ts\">\nimport MRadio from './MRadio.vue'\n\ninterface Option {\n label: string\n value: unknown\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: unknown\n options: Option[]\n label?: string\n direction?: 'column' | 'row'\n disabled?: boolean\n color?: 'primary' | 'secondary' | 'tertiary' | 'error'\n}>(), { direction: 'column', disabled: false, color: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [unknown] }>()\n</script>\n\n<template>\n <div class=\"flex flex-col gap-2\">\n <span v-if=\"label\" class=\"text-label-large text-on-surface-variant\">{{ label }}</span>\n <div\n class=\"flex gap-4\"\n :class=\"direction === 'row' ? 'flex-row flex-wrap' : 'flex-col'\"\n >\n <MRadio\n v-for=\"opt in options\"\n :key=\"String(opt.value)\"\n :model-value=\"modelValue\"\n :value=\"opt.value\"\n :label=\"opt.label\"\n :color=\"color\"\n :disabled=\"disabled || !!opt.disabled\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MRadio from './MRadio.vue'\n\ninterface Option {\n label: string\n value: unknown\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: unknown\n options: Option[]\n label?: string\n direction?: 'column' | 'row'\n disabled?: boolean\n color?: 'primary' | 'secondary' | 'tertiary' | 'error'\n}>(), { direction: 'column', disabled: false, color: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [unknown] }>()\n</script>\n\n<template>\n <div class=\"flex flex-col gap-2\">\n <span v-if=\"label\" class=\"text-label-large text-on-surface-variant\">{{ label }}</span>\n <div\n class=\"flex gap-4\"\n :class=\"direction === 'row' ? 'flex-row flex-wrap' : 'flex-col'\"\n >\n <MRadio\n v-for=\"opt in options\"\n :key=\"String(opt.value)\"\n :model-value=\"modelValue\"\n :value=\"opt.value\"\n :label=\"opt.label\"\n :color=\"color\"\n :disabled=\"disabled || !!opt.disabled\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n modelValue: number\n max?: number\n size?: number\n readonly?: boolean\n disabled?: boolean\n color?: string\n icon?: string\n halfIncrements?: boolean\n}>(), { max: 5, size: 28, color: 'primary', icon: 'star', halfIncrements: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [number] }>()\n\nconst hovered = ref(-1)\n\nconst colorClass = computed(() => {\n const map: Record<string, string> = {\n primary: 'text-primary',\n secondary: 'text-secondary',\n tertiary: 'text-tertiary',\n error: 'text-error',\n }\n return map[props.color] || ''\n})\n\nconst customStyle = computed(() => {\n if (['primary', 'secondary', 'tertiary', 'error'].includes(props.color)) return undefined\n return { color: props.color }\n})\n\nfunction valueAt(index: number, e?: MouseEvent) {\n if (!props.halfIncrements) return index + 1\n if (!e) return index + 1\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()\n const half = (e.clientX - rect.left) < rect.width / 2\n return half ? index + 0.5 : index + 1\n}\n\nfunction onClick(index: number, e: MouseEvent) {\n if (props.readonly || props.disabled) return\n const v = valueAt(index, e)\n emit('update:modelValue', v === props.modelValue ? 0 : v)\n}\n\nfunction onMove(index: number, e: MouseEvent) {\n if (props.readonly || props.disabled) return\n hovered.value = valueAt(index, e)\n}\n\nfunction onLeave() {\n hovered.value = -1\n}\n\nfunction iconName(index: number) {\n const active = hovered.value >= 0 ? hovered.value : props.modelValue\n if (index + 1 <= active) return props.icon\n if (props.halfIncrements && index + 0.5 <= active) return props.icon + '_half'\n return props.icon + '_border' // outlined variant not available for all icons\n}\n\nfunction isFilled(index: number) {\n const active = hovered.value >= 0 ? hovered.value : props.modelValue\n return index + 1 <= active || (props.halfIncrements && index + 0.5 <= active)\n}\n</script>\n\n<template>\n <div\n class=\"inline-flex items-center gap-0.5\"\n :class=\"disabled ? 'opacity-[0.38]' : ''\"\n @mouseleave=\"onLeave\"\n >\n <button\n v-for=\"i in max\"\n :key=\"i\"\n type=\"button\"\n class=\"relative inline-flex items-center justify-center rounded-full p-0.5 transition-transform duration-100\"\n :class=\"[\n readonly || disabled ? 'cursor-default' : 'cursor-pointer hover:scale-110',\n ]\"\n :style=\"customStyle\"\n :disabled=\"disabled\"\n @click=\"onClick(i - 1, $event)\"\n @mousemove=\"onMove(i - 1, $event)\"\n >\n <!-- Filled star -->\n <MIcon\n v-if=\"isFilled(i - 1)\"\n :name=\"icon\"\n :size=\"size\"\n :class=\"colorClass\"\n :style=\"customStyle\"\n style=\"font-variation-settings: 'FILL' 1\"\n />\n <!-- Empty star -->\n <MIcon\n v-else\n :name=\"icon\"\n :size=\"size\"\n class=\"text-on-surface-variant/40\"\n />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n modelValue: number\n max?: number\n size?: number\n readonly?: boolean\n disabled?: boolean\n color?: string\n icon?: string\n halfIncrements?: boolean\n}>(), { max: 5, size: 28, color: 'primary', icon: 'star', halfIncrements: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [number] }>()\n\nconst hovered = ref(-1)\n\nconst colorClass = computed(() => {\n const map: Record<string, string> = {\n primary: 'text-primary',\n secondary: 'text-secondary',\n tertiary: 'text-tertiary',\n error: 'text-error',\n }\n return map[props.color] || ''\n})\n\nconst customStyle = computed(() => {\n if (['primary', 'secondary', 'tertiary', 'error'].includes(props.color)) return undefined\n return { color: props.color }\n})\n\nfunction valueAt(index: number, e?: MouseEvent) {\n if (!props.halfIncrements) return index + 1\n if (!e) return index + 1\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()\n const half = (e.clientX - rect.left) < rect.width / 2\n return half ? index + 0.5 : index + 1\n}\n\nfunction onClick(index: number, e: MouseEvent) {\n if (props.readonly || props.disabled) return\n const v = valueAt(index, e)\n emit('update:modelValue', v === props.modelValue ? 0 : v)\n}\n\nfunction onMove(index: number, e: MouseEvent) {\n if (props.readonly || props.disabled) return\n hovered.value = valueAt(index, e)\n}\n\nfunction onLeave() {\n hovered.value = -1\n}\n\nfunction iconName(index: number) {\n const active = hovered.value >= 0 ? hovered.value : props.modelValue\n if (index + 1 <= active) return props.icon\n if (props.halfIncrements && index + 0.5 <= active) return props.icon + '_half'\n return props.icon + '_border' // outlined variant not available for all icons\n}\n\nfunction isFilled(index: number) {\n const active = hovered.value >= 0 ? hovered.value : props.modelValue\n return index + 1 <= active || (props.halfIncrements && index + 0.5 <= active)\n}\n</script>\n\n<template>\n <div\n class=\"inline-flex items-center gap-0.5\"\n :class=\"disabled ? 'opacity-[0.38]' : ''\"\n @mouseleave=\"onLeave\"\n >\n <button\n v-for=\"i in max\"\n :key=\"i\"\n type=\"button\"\n class=\"relative inline-flex items-center justify-center rounded-full p-0.5 transition-transform duration-100\"\n :class=\"[\n readonly || disabled ? 'cursor-default' : 'cursor-pointer hover:scale-110',\n ]\"\n :style=\"customStyle\"\n :disabled=\"disabled\"\n @click=\"onClick(i - 1, $event)\"\n @mousemove=\"onMove(i - 1, $event)\"\n >\n <!-- Filled star -->\n <MIcon\n v-if=\"isFilled(i - 1)\"\n :name=\"icon\"\n :size=\"size\"\n :class=\"colorClass\"\n :style=\"customStyle\"\n style=\"font-variation-settings: 'FILL' 1\"\n />\n <!-- Empty star -->\n <MIcon\n v-else\n :name=\"icon\"\n :size=\"size\"\n class=\"text-on-surface-variant/40\"\n />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n status: 'success' | 'error' | 'warning' | 'info' | '404' | '403' | '500'\n title?: string\n description?: string\n}>(), {})\n\nconst config = computed(() => {\n switch (props.status) {\n case 'success': return { icon: 'check_circle', bg: 'bg-success-container', text: 'text-on-success-container', defaultTitle: 'Operación exitosa', defaultDesc: 'La acción se completó correctamente.' }\n case 'error': return { icon: 'error', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Algo salió mal', defaultDesc: 'Ocurrió un error inesperado. Inténtalo de nuevo.' }\n case 'warning': return { icon: 'warning', bg: 'bg-tertiary-container', text: 'text-on-tertiary-container', defaultTitle: 'Atención', defaultDesc: 'Hay algo que requiere tu atención.' }\n case 'info': return { icon: 'info', bg: 'bg-primary-container', text: 'text-on-primary-container', defaultTitle: 'Información', defaultDesc: '' }\n case '404': return { icon: 'search_off', bg: 'bg-surface-container-high', text: 'text-on-surface-variant', defaultTitle: 'Página no encontrada', defaultDesc: 'La página que buscas no existe o fue movida.' }\n case '403': return { icon: 'lock', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Acceso denegado', defaultDesc: 'No tienes permisos para ver este recurso.' }\n case '500': return { icon: 'cloud_off', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Error del servidor', defaultDesc: 'El servidor no pudo procesar la solicitud.' }\n default: return { icon: 'info', bg: 'bg-surface-container-high', text: 'text-on-surface-variant', defaultTitle: '', defaultDesc: '' }\n }\n})\n\nconst httpCode = computed(() => {\n if (props.status === '404' || props.status === '403' || props.status === '500') return props.status\n return null\n})\n</script>\n\n<template>\n <div class=\"flex flex-col items-center justify-center gap-4 py-14 text-center\">\n <!-- HTTP code -->\n <span v-if=\"httpCode\" class=\"text-display-small font-medium text-on-surface-variant/30\">\n {{ httpCode }}\n </span>\n\n <!-- Icon -->\n <div class=\"flex h-20 w-20 items-center justify-center rounded-full\" :class=\"[config.bg, config.text]\">\n <MIcon :name=\"config.icon\" :size=\"40\" />\n </div>\n\n <!-- Title -->\n <h2 class=\"text-headline-small font-medium text-on-surface\">\n {{ title ?? config.defaultTitle }}\n </h2>\n\n <!-- Description -->\n <p v-if=\"description ?? config.defaultDesc\" class=\"max-w-md text-body-large text-on-surface-variant\">\n {{ description ?? config.defaultDesc }}\n </p>\n\n <!-- Actions slot -->\n <div v-if=\"$slots.actions\" class=\"mt-2 flex flex-wrap items-center justify-center gap-3\">\n <slot name=\"actions\" />\n </div>\n\n <!-- Extra content -->\n <div v-if=\"$slots.default\" class=\"mt-2\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n status: 'success' | 'error' | 'warning' | 'info' | '404' | '403' | '500'\n title?: string\n description?: string\n}>(), {})\n\nconst config = computed(() => {\n switch (props.status) {\n case 'success': return { icon: 'check_circle', bg: 'bg-success-container', text: 'text-on-success-container', defaultTitle: 'Operación exitosa', defaultDesc: 'La acción se completó correctamente.' }\n case 'error': return { icon: 'error', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Algo salió mal', defaultDesc: 'Ocurrió un error inesperado. Inténtalo de nuevo.' }\n case 'warning': return { icon: 'warning', bg: 'bg-tertiary-container', text: 'text-on-tertiary-container', defaultTitle: 'Atención', defaultDesc: 'Hay algo que requiere tu atención.' }\n case 'info': return { icon: 'info', bg: 'bg-primary-container', text: 'text-on-primary-container', defaultTitle: 'Información', defaultDesc: '' }\n case '404': return { icon: 'search_off', bg: 'bg-surface-container-high', text: 'text-on-surface-variant', defaultTitle: 'Página no encontrada', defaultDesc: 'La página que buscas no existe o fue movida.' }\n case '403': return { icon: 'lock', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Acceso denegado', defaultDesc: 'No tienes permisos para ver este recurso.' }\n case '500': return { icon: 'cloud_off', bg: 'bg-error-container', text: 'text-on-error-container', defaultTitle: 'Error del servidor', defaultDesc: 'El servidor no pudo procesar la solicitud.' }\n default: return { icon: 'info', bg: 'bg-surface-container-high', text: 'text-on-surface-variant', defaultTitle: '', defaultDesc: '' }\n }\n})\n\nconst httpCode = computed(() => {\n if (props.status === '404' || props.status === '403' || props.status === '500') return props.status\n return null\n})\n</script>\n\n<template>\n <div class=\"flex flex-col items-center justify-center gap-4 py-14 text-center\">\n <!-- HTTP code -->\n <span v-if=\"httpCode\" class=\"text-display-small font-medium text-on-surface-variant/30\">\n {{ httpCode }}\n </span>\n\n <!-- Icon -->\n <div class=\"flex h-20 w-20 items-center justify-center rounded-full\" :class=\"[config.bg, config.text]\">\n <MIcon :name=\"config.icon\" :size=\"40\" />\n </div>\n\n <!-- Title -->\n <h2 class=\"text-headline-small font-medium text-on-surface\">\n {{ title ?? config.defaultTitle }}\n </h2>\n\n <!-- Description -->\n <p v-if=\"description ?? config.defaultDesc\" class=\"max-w-md text-body-large text-on-surface-variant\">\n {{ description ?? config.defaultDesc }}\n </p>\n\n <!-- Actions slot -->\n <div v-if=\"$slots.actions\" class=\"mt-2 flex flex-wrap items-center justify-center gap-3\">\n <slot name=\"actions\" />\n </div>\n\n <!-- Extra content -->\n <div v-if=\"$slots.default\" class=\"mt-2\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface SchedulerEvent {\n id: string | number\n title: string\n start: string\n end: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n}\n\nconst props = withDefaults(\n defineProps<{\n events?: SchedulerEvent[]\n view?: 'week' | 'day'\n startHour?: number\n endHour?: number\n locale?: string\n }>(),\n {\n events: () => [],\n view: 'week',\n startHour: 7,\n endHour: 22,\n locale: 'es-ES',\n },\n)\n\nconst emit = defineEmits<{\n eventClick: [SchedulerEvent]\n slotClick: [{ date: string; hour: number }]\n}>()\n\nconst currentDate = ref(new Date())\nconst currentView = ref(props.view)\n\nconst hours = computed(() =>\n Array.from({ length: props.endHour - props.startHour }, (_, i) => props.startHour + i),\n)\n\nfunction startOfWeek(d: Date) {\n const dt = new Date(d)\n const day = dt.getDay()\n const diff = day === 0 ? -6 : 1 - day\n dt.setDate(dt.getDate() + diff)\n dt.setHours(0, 0, 0, 0)\n return dt\n}\n\nconst weekDays = computed(() => {\n const start = startOfWeek(currentDate.value)\n return Array.from({ length: 7 }, (_, i) => {\n const d = new Date(start)\n d.setDate(d.getDate() + i)\n return d\n })\n})\n\nconst visibleDays = computed(() =>\n currentView.value === 'day' ? [currentDate.value] : weekDays.value,\n)\n\nconst todayIso = (() => {\n const d = new Date()\n return fmt(d)\n})()\n\nfunction fmt(d: Date) {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n}\n\nconst dayFormat = new Intl.DateTimeFormat(props.locale, { weekday: 'short' })\nconst dateFormat = new Intl.DateTimeFormat(props.locale, { day: 'numeric' })\n\nconst headerLabel = computed(() => {\n if (currentView.value === 'day') {\n return new Intl.DateTimeFormat(props.locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })\n .format(currentDate.value)\n }\n const start = weekDays.value[0]!\n const end = weekDays.value[6]!\n const f = new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short' })\n return `${f.format(start)} – ${f.format(end)}, ${end.getFullYear()}`\n})\n\nfunction navigate(delta: number) {\n const d = new Date(currentDate.value)\n if (currentView.value === 'week') d.setDate(d.getDate() + delta * 7)\n else d.setDate(d.getDate() + delta)\n currentDate.value = d\n}\n\nfunction goToday() { currentDate.value = new Date() }\n\nfunction eventsForDayHour(day: Date, hour: number) {\n const dayStr = fmt(day)\n return props.events.filter((ev) => {\n const start = new Date(ev.start)\n const end = new Date(ev.end)\n const evDay = fmt(start)\n if (evDay !== dayStr) return false\n const evStartHour = start.getHours()\n const evEndHour = end.getHours() + (end.getMinutes() > 0 ? 1 : 0)\n return hour >= evStartHour && hour < evEndHour\n })\n}\n\nfunction isEventStart(ev: SchedulerEvent, hour: number) {\n return new Date(ev.start).getHours() === hour\n}\n\nfunction eventDuration(ev: SchedulerEvent) {\n const start = new Date(ev.start)\n const end = new Date(ev.end)\n return Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 3600000))\n}\n\nfunction timeLabel(ev: SchedulerEvent) {\n const f = new Intl.DateTimeFormat(props.locale, { hour: '2-digit', minute: '2-digit' })\n return `${f.format(new Date(ev.start))} – ${f.format(new Date(ev.end))}`\n}\n\nconst eventColors: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container border-primary/30',\n secondary: 'bg-secondary-container text-on-secondary-container border-secondary/30',\n tertiary: 'bg-tertiary-container text-on-tertiary-container border-tertiary/30',\n error: 'bg-error-container text-on-error-container border-error/30',\n success: 'bg-success-container text-on-success-container border-success/30',\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-3\">\n <div class=\"flex items-center gap-1\">\n <MIconButton icon=\"chevron_left\" label=\"Anterior\" :size=\"36\" @click=\"navigate(-1)\" />\n <MIconButton icon=\"chevron_right\" label=\"Siguiente\" :size=\"36\" @click=\"navigate(1)\" />\n <button\n type=\"button\"\n class=\"ml-2 cursor-pointer rounded-full border border-outline px-3 py-1 text-label-medium text-on-surface transition-colors hover:bg-on-surface/8\"\n @click=\"goToday\"\n >\n Hoy\n </button>\n </div>\n\n <h3 class=\"text-title-medium font-medium capitalize text-on-surface\">{{ headerLabel }}</h3>\n\n <div class=\"flex rounded-full bg-surface-container-high p-0.5\">\n <button\n v-for=\"v in (['day', 'week'] as const)\"\n :key=\"v\"\n type=\"button\"\n class=\"cursor-pointer rounded-full px-3 py-1 text-label-medium transition-all duration-150\"\n :class=\"currentView === v ? 'bg-secondary-container text-on-secondary-container shadow-elevation-1' : 'text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"currentView = v\"\n >\n {{ v === 'day' ? 'Día' : 'Semana' }}\n </button>\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"overflow-auto\">\n <table class=\"w-full border-collapse\">\n <!-- Day headers -->\n <thead>\n <tr>\n <th class=\"sticky top-0 z-10 w-16 border-b border-r border-outline-variant bg-surface-container p-2\" />\n <th\n v-for=\"day in visibleDays\"\n :key=\"fmt(day)\"\n class=\"sticky top-0 z-10 border-b border-r border-outline-variant bg-surface-container px-2 py-2 text-center last:border-r-0\"\n :class=\"fmt(day) === todayIso ? 'bg-primary-container/30' : ''\"\n >\n <div class=\"text-label-small uppercase text-on-surface-variant\">{{ dayFormat.format(day) }}</div>\n <div\n class=\"mx-auto mt-0.5 flex h-8 w-8 items-center justify-center rounded-full text-title-medium\"\n :class=\"fmt(day) === todayIso ? 'bg-primary text-on-primary font-medium' : 'text-on-surface'\"\n >\n {{ dateFormat.format(day) }}\n </div>\n </th>\n </tr>\n </thead>\n\n <tbody>\n <tr v-for=\"hour in hours\" :key=\"hour\">\n <!-- Hour label -->\n <td class=\"w-16 border-r border-b border-outline-variant/50 p-0 pr-2 text-right align-top\">\n <span class=\"relative -top-2.5 text-label-small text-on-surface-variant\">\n {{ String(hour).padStart(2, '0') }}:00\n </span>\n </td>\n\n <!-- Day cells -->\n <td\n v-for=\"day in visibleDays\"\n :key=\"fmt(day)\"\n class=\"relative h-14 border-r border-b border-outline-variant/50 p-0 last:border-r-0\"\n :class=\"fmt(day) === todayIso ? 'bg-primary-container/[0.05]' : ''\"\n @click=\"emit('slotClick', { date: fmt(day), hour })\"\n >\n <template v-for=\"ev in eventsForDayHour(day, hour)\" :key=\"ev.id\">\n <button\n v-if=\"isEventStart(ev, hour)\"\n type=\"button\"\n class=\"absolute inset-x-0.5 top-0.5 z-[5] cursor-pointer overflow-hidden rounded border-l-[3px] px-2 py-1 text-left transition-opacity hover:opacity-90\"\n :class=\"eventColors[ev.color ?? 'primary']\"\n :style=\"{ height: `calc(${eventDuration(ev) * 100}% - 4px)` }\"\n @click.stop=\"emit('eventClick', ev)\"\n >\n <p class=\"truncate text-label-small font-medium\">{{ ev.title }}</p>\n <p class=\"truncate text-label-small opacity-70\">{{ timeLabel(ev) }}</p>\n </button>\n </template>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport MIconButton from './MIconButton.vue'\n\nexport interface SchedulerEvent {\n id: string | number\n title: string\n start: string\n end: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n}\n\nconst props = withDefaults(\n defineProps<{\n events?: SchedulerEvent[]\n view?: 'week' | 'day'\n startHour?: number\n endHour?: number\n locale?: string\n }>(),\n {\n events: () => [],\n view: 'week',\n startHour: 7,\n endHour: 22,\n locale: 'es-ES',\n },\n)\n\nconst emit = defineEmits<{\n eventClick: [SchedulerEvent]\n slotClick: [{ date: string; hour: number }]\n}>()\n\nconst currentDate = ref(new Date())\nconst currentView = ref(props.view)\n\nconst hours = computed(() =>\n Array.from({ length: props.endHour - props.startHour }, (_, i) => props.startHour + i),\n)\n\nfunction startOfWeek(d: Date) {\n const dt = new Date(d)\n const day = dt.getDay()\n const diff = day === 0 ? -6 : 1 - day\n dt.setDate(dt.getDate() + diff)\n dt.setHours(0, 0, 0, 0)\n return dt\n}\n\nconst weekDays = computed(() => {\n const start = startOfWeek(currentDate.value)\n return Array.from({ length: 7 }, (_, i) => {\n const d = new Date(start)\n d.setDate(d.getDate() + i)\n return d\n })\n})\n\nconst visibleDays = computed(() =>\n currentView.value === 'day' ? [currentDate.value] : weekDays.value,\n)\n\nconst todayIso = (() => {\n const d = new Date()\n return fmt(d)\n})()\n\nfunction fmt(d: Date) {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n}\n\nconst dayFormat = new Intl.DateTimeFormat(props.locale, { weekday: 'short' })\nconst dateFormat = new Intl.DateTimeFormat(props.locale, { day: 'numeric' })\n\nconst headerLabel = computed(() => {\n if (currentView.value === 'day') {\n return new Intl.DateTimeFormat(props.locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })\n .format(currentDate.value)\n }\n const start = weekDays.value[0]!\n const end = weekDays.value[6]!\n const f = new Intl.DateTimeFormat(props.locale, { day: 'numeric', month: 'short' })\n return `${f.format(start)} – ${f.format(end)}, ${end.getFullYear()}`\n})\n\nfunction navigate(delta: number) {\n const d = new Date(currentDate.value)\n if (currentView.value === 'week') d.setDate(d.getDate() + delta * 7)\n else d.setDate(d.getDate() + delta)\n currentDate.value = d\n}\n\nfunction goToday() { currentDate.value = new Date() }\n\nfunction eventsForDayHour(day: Date, hour: number) {\n const dayStr = fmt(day)\n return props.events.filter((ev) => {\n const start = new Date(ev.start)\n const end = new Date(ev.end)\n const evDay = fmt(start)\n if (evDay !== dayStr) return false\n const evStartHour = start.getHours()\n const evEndHour = end.getHours() + (end.getMinutes() > 0 ? 1 : 0)\n return hour >= evStartHour && hour < evEndHour\n })\n}\n\nfunction isEventStart(ev: SchedulerEvent, hour: number) {\n return new Date(ev.start).getHours() === hour\n}\n\nfunction eventDuration(ev: SchedulerEvent) {\n const start = new Date(ev.start)\n const end = new Date(ev.end)\n return Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 3600000))\n}\n\nfunction timeLabel(ev: SchedulerEvent) {\n const f = new Intl.DateTimeFormat(props.locale, { hour: '2-digit', minute: '2-digit' })\n return `${f.format(new Date(ev.start))} – ${f.format(new Date(ev.end))}`\n}\n\nconst eventColors: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container border-primary/30',\n secondary: 'bg-secondary-container text-on-secondary-container border-secondary/30',\n tertiary: 'bg-tertiary-container text-on-tertiary-container border-tertiary/30',\n error: 'bg-error-container text-on-error-container border-error/30',\n success: 'bg-success-container text-on-success-container border-success/30',\n}\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-3\">\n <div class=\"flex items-center gap-1\">\n <MIconButton icon=\"chevron_left\" label=\"Anterior\" :size=\"36\" @click=\"navigate(-1)\" />\n <MIconButton icon=\"chevron_right\" label=\"Siguiente\" :size=\"36\" @click=\"navigate(1)\" />\n <button\n type=\"button\"\n class=\"ml-2 cursor-pointer rounded-full border border-outline px-3 py-1 text-label-medium text-on-surface transition-colors hover:bg-on-surface/8\"\n @click=\"goToday\"\n >\n Hoy\n </button>\n </div>\n\n <h3 class=\"text-title-medium font-medium capitalize text-on-surface\">{{ headerLabel }}</h3>\n\n <div class=\"flex rounded-full bg-surface-container-high p-0.5\">\n <button\n v-for=\"v in (['day', 'week'] as const)\"\n :key=\"v\"\n type=\"button\"\n class=\"cursor-pointer rounded-full px-3 py-1 text-label-medium transition-all duration-150\"\n :class=\"currentView === v ? 'bg-secondary-container text-on-secondary-container shadow-elevation-1' : 'text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"currentView = v\"\n >\n {{ v === 'day' ? 'Día' : 'Semana' }}\n </button>\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"overflow-auto\">\n <table class=\"w-full border-collapse\">\n <!-- Day headers -->\n <thead>\n <tr>\n <th class=\"sticky top-0 z-10 w-16 border-b border-r border-outline-variant bg-surface-container p-2\" />\n <th\n v-for=\"day in visibleDays\"\n :key=\"fmt(day)\"\n class=\"sticky top-0 z-10 border-b border-r border-outline-variant bg-surface-container px-2 py-2 text-center last:border-r-0\"\n :class=\"fmt(day) === todayIso ? 'bg-primary-container/30' : ''\"\n >\n <div class=\"text-label-small uppercase text-on-surface-variant\">{{ dayFormat.format(day) }}</div>\n <div\n class=\"mx-auto mt-0.5 flex h-8 w-8 items-center justify-center rounded-full text-title-medium\"\n :class=\"fmt(day) === todayIso ? 'bg-primary text-on-primary font-medium' : 'text-on-surface'\"\n >\n {{ dateFormat.format(day) }}\n </div>\n </th>\n </tr>\n </thead>\n\n <tbody>\n <tr v-for=\"hour in hours\" :key=\"hour\">\n <!-- Hour label -->\n <td class=\"w-16 border-r border-b border-outline-variant/50 p-0 pr-2 text-right align-top\">\n <span class=\"relative -top-2.5 text-label-small text-on-surface-variant\">\n {{ String(hour).padStart(2, '0') }}:00\n </span>\n </td>\n\n <!-- Day cells -->\n <td\n v-for=\"day in visibleDays\"\n :key=\"fmt(day)\"\n class=\"relative h-14 border-r border-b border-outline-variant/50 p-0 last:border-r-0\"\n :class=\"fmt(day) === todayIso ? 'bg-primary-container/[0.05]' : ''\"\n @click=\"emit('slotClick', { date: fmt(day), hour })\"\n >\n <template v-for=\"ev in eventsForDayHour(day, hour)\" :key=\"ev.id\">\n <button\n v-if=\"isEventStart(ev, hour)\"\n type=\"button\"\n class=\"absolute inset-x-0.5 top-0.5 z-[5] cursor-pointer overflow-hidden rounded border-l-[3px] px-2 py-1 text-left transition-opacity hover:opacity-90\"\n :class=\"eventColors[ev.color ?? 'primary']\"\n :style=\"{ height: `calc(${eventDuration(ev) * 100}% - 4px)` }\"\n @click.stop=\"emit('eventClick', ev)\"\n >\n <p class=\"truncate text-label-small font-medium\">{{ ev.title }}</p>\n <p class=\"truncate text-label-small opacity-70\">{{ timeLabel(ev) }}</p>\n </button>\n </template>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nexport interface SegmentedOption {\n value: string | number\n label: string\n icon?: string\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number | (string | number)[]\n options: SegmentedOption[]\n multiSelect?: boolean\n density?: 'default' | 'comfortable' | 'compact'\n color?: 'primary' | 'secondary' | 'tertiary'\n}>(), { multiSelect: false, density: 'default', color: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number | (string | number)[]] }>()\n\nfunction isSelected(value: string | number, modelValue: string | number | (string | number)[]) {\n return Array.isArray(modelValue) ? modelValue.includes(value) : modelValue === value\n}\n\nfunction toggle(opt: SegmentedOption, modelValue: string | number | (string | number)[], multi: boolean) {\n if (opt.disabled) return\n if (multi) {\n const arr = Array.isArray(modelValue) ? [...modelValue] : [modelValue]\n const idx = arr.indexOf(opt.value)\n if (idx >= 0) arr.splice(idx, 1)\n else arr.push(opt.value)\n emit('update:modelValue', arr)\n } else {\n emit('update:modelValue', opt.value)\n }\n}\n</script>\n\n<template>\n <div\n class=\"inline-flex overflow-hidden rounded-full border border-outline\"\n role=\"group\"\n >\n <button\n v-for=\"(opt, i) in options\"\n :key=\"opt.value\"\n type=\"button\"\n class=\"relative inline-flex items-center justify-center gap-2 text-label-large font-medium transition-[background-color,color] duration-150 outline-none before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 enabled:hover:before:opacity-[0.08] enabled:active:before:opacity-[0.12]\"\n :class=\"[\n density === 'compact' ? 'h-8 px-3' : density === 'comfortable' ? 'h-10 px-4' : 'h-10 px-6',\n i > 0 ? 'border-l border-outline' : '',\n opt.disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer',\n isSelected(opt.value, modelValue)\n ? color === 'secondary'\n ? 'bg-secondary-container text-on-secondary-container'\n : color === 'tertiary'\n ? 'bg-tertiary-container text-on-tertiary-container'\n : 'bg-secondary-container text-on-secondary-container'\n : 'text-on-surface',\n ]\"\n :disabled=\"opt.disabled\"\n :aria-pressed=\"isSelected(opt.value, modelValue)\"\n @click=\"toggle(opt, modelValue, multiSelect)\"\n >\n <MIcon\n v-if=\"isSelected(opt.value, modelValue)\"\n name=\"check\"\n :size=\"18\"\n class=\"transition-transform duration-150\"\n />\n <MIcon v-else-if=\"opt.icon\" :name=\"opt.icon\" :size=\"18\" />\n <span>{{ opt.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\n\nexport interface SegmentedOption {\n value: string | number\n label: string\n icon?: string\n disabled?: boolean\n}\n\nwithDefaults(defineProps<{\n modelValue: string | number | (string | number)[]\n options: SegmentedOption[]\n multiSelect?: boolean\n density?: 'default' | 'comfortable' | 'compact'\n color?: 'primary' | 'secondary' | 'tertiary'\n}>(), { multiSelect: false, density: 'default', color: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number | (string | number)[]] }>()\n\nfunction isSelected(value: string | number, modelValue: string | number | (string | number)[]) {\n return Array.isArray(modelValue) ? modelValue.includes(value) : modelValue === value\n}\n\nfunction toggle(opt: SegmentedOption, modelValue: string | number | (string | number)[], multi: boolean) {\n if (opt.disabled) return\n if (multi) {\n const arr = Array.isArray(modelValue) ? [...modelValue] : [modelValue]\n const idx = arr.indexOf(opt.value)\n if (idx >= 0) arr.splice(idx, 1)\n else arr.push(opt.value)\n emit('update:modelValue', arr)\n } else {\n emit('update:modelValue', opt.value)\n }\n}\n</script>\n\n<template>\n <div\n class=\"inline-flex overflow-hidden rounded-full border border-outline\"\n role=\"group\"\n >\n <button\n v-for=\"(opt, i) in options\"\n :key=\"opt.value\"\n type=\"button\"\n class=\"relative inline-flex items-center justify-center gap-2 text-label-large font-medium transition-[background-color,color] duration-150 outline-none before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 enabled:hover:before:opacity-[0.08] enabled:active:before:opacity-[0.12]\"\n :class=\"[\n density === 'compact' ? 'h-8 px-3' : density === 'comfortable' ? 'h-10 px-4' : 'h-10 px-6',\n i > 0 ? 'border-l border-outline' : '',\n opt.disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer',\n isSelected(opt.value, modelValue)\n ? color === 'secondary'\n ? 'bg-secondary-container text-on-secondary-container'\n : color === 'tertiary'\n ? 'bg-tertiary-container text-on-tertiary-container'\n : 'bg-secondary-container text-on-secondary-container'\n : 'text-on-surface',\n ]\"\n :disabled=\"opt.disabled\"\n :aria-pressed=\"isSelected(opt.value, modelValue)\"\n @click=\"toggle(opt, modelValue, multiSelect)\"\n >\n <MIcon\n v-if=\"isSelected(opt.value, modelValue)\"\n name=\"check\"\n :size=\"18\"\n class=\"transition-transform duration-150\"\n />\n <MIcon v-else-if=\"opt.icon\" :name=\"opt.icon\" :size=\"18\" />\n <span>{{ opt.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, onMounted, onUnmounted } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number | null\n options: { label: string; value: string | number; disabled?: boolean }[]\n label?: string\n placeholder?: string\n variant?: 'filled' | 'outlined'\n disabled?: boolean\n error?: string\n hint?: string\n required?: boolean\n leadingIcon?: string\n fieldBg?: string\n }>(),\n {\n modelValue: null,\n variant: 'filled',\n disabled: false,\n required: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number] }>()\n\nconst id = useId()\nconst open = ref(false)\nconst fieldEl = ref<HTMLElement | null>(null)\nconst { resolvedFieldBg } = useFieldBg(fieldEl, () => props.fieldBg)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px', width: '0px' })\n\nconst hasValue = computed(() => props.modelValue !== null && props.modelValue !== '')\nconst selectedLabel = computed(\n () => props.options.find((o) => o.value === props.modelValue)?.label ?? '',\n)\n\nfunction computeDropPos() {\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const dropH = Math.min(240, props.options.length * 52 + 8)\n const openAbove = spaceBelow < dropH && rect.top > dropH\n dropPos.value = {\n top: openAbove ? `${rect.top - 4 - dropH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n }\n}\n\nfunction toggle() {\n if (props.disabled) return\n if (!open.value) computeDropPos()\n open.value = !open.value\n}\n\nfunction select(opt: { value: string | number; disabled?: boolean }) {\n if (opt.disabled) return\n emit('update:modelValue', opt.value)\n open.value = false\n}\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!fieldEl.value?.contains(t) && !dropdownEl.value?.contains(t)) open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n // Scrolling inside the dropdown list itself — do nothing\n if (dropdownEl.value?.contains(e.target as Node)) return\n // Recompute position to track the trigger element as the page scrolls\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n // Only close if the trigger has scrolled completely out of the viewport\n if (rect.bottom < 0 || rect.top > window.innerHeight) {\n open.value = false\n return\n }\n computeDropPos()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') { open.value = false; return }\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); return }\n if (!open.value) return\n const opts = props.options.filter((o) => !o.disabled)\n const idx = opts.findIndex((o) => o.value === props.modelValue)\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = opts[(idx + 1) % opts.length]\n if (next) emit('update:modelValue', next.value)\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n const prev = opts[(idx - 1 + opts.length) % opts.length]\n if (prev) emit('update:modelValue', prev.value)\n }\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst triggerClasses = computed(() => {\n const pl = props.leadingIcon ? 'pl-12' : 'pl-4'\n const base = [\n 'flex w-full cursor-pointer items-center pr-10 text-body-large transition-[border-color,border-width] duration-150',\n pl,\n ]\n\n if (props.variant === 'outlined') {\n return [\n ...base,\n 'h-14 rounded-sm border bg-transparent',\n open.value\n ? (props.error ? 'border-2 border-error' : 'border-2 border-primary')\n : (props.error ? 'border-error' : 'border-outline hover:border-on-surface'),\n ].join(' ')\n }\n\n return [\n ...base,\n 'h-14 rounded-t-sm bg-surface-container-highest border-b pt-6 pb-2',\n open.value\n ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')\n : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),\n ].join(' ')\n})\n\nconst isFloated = computed(() => hasValue.value || open.value)\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? (props.variant === 'outlined' ? 'left-11' : 'left-12')\n : (props.variant === 'outlined' ? 'left-3' : 'left-4')\n\n const floated = props.variant === 'outlined'\n ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'\n : 'top-2 translate-y-0 text-label-small'\n\n const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'\n\n return [\n 'pointer-events-none absolute right-10 truncate transition-all duration-200',\n left,\n isFloated.value ? floated : unFloated,\n open.value\n ? (props.error ? 'text-error' : 'text-primary')\n : (props.error ? 'text-error' : 'text-on-surface-variant'),\n ].join(' ')\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div\n ref=\"fieldEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <!-- Leading icon -->\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <!-- Custom trigger -->\n <div\n :id=\"id\"\n :tabindex=\"disabled ? -1 : 0\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled\"\n :class=\"[triggerClasses, disabled ? 'pointer-events-none opacity-[0.38]' : '']\"\n @click=\"toggle\"\n @keydown=\"onKeydown\"\n >\n <span v-if=\"hasValue\" class=\"text-on-surface\">{{ selectedLabel }}</span>\n </div>\n\n <!-- Floating label -->\n <label :for=\"id\" :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <!-- Arrow icon -->\n <div class=\"pointer-events-none absolute right-2 top-1/2 -translate-y-1/2\">\n <MIcon\n :name=\"open ? 'arrow_drop_up' : 'arrow_drop_down'\"\n :size=\"24\"\n class=\"text-on-surface-variant transition-transform duration-200\"\n />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n\n <!-- Dropdown teleported to body to escape overflow clipping -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n enter-to-class=\"opacity-100 translate-y-0 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-from-class=\"opacity-100 translate-y-0 scale-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] max-h-60 overflow-auto rounded-sm bg-surface-container py-1 shadow-elevation-2\"\n :style=\"dropPos\"\n >\n <div\n v-for=\"opt in options\"\n :key=\"opt.value\"\n class=\"flex cursor-pointer items-center gap-3 px-4 py-3 text-body-large\"\n :class=\"[\n opt.disabled\n ? 'cursor-not-allowed opacity-38 text-on-surface'\n : 'text-on-surface hover:bg-on-surface/8',\n opt.value === modelValue ? 'bg-primary/8 text-primary font-medium' : '',\n ]\"\n @click=\"select(opt)\"\n >\n <MIcon\n v-if=\"opt.value === modelValue\"\n name=\"check\"\n :size=\"18\"\n class=\"shrink-0 text-primary\"\n />\n <span v-else class=\"w-[18px] shrink-0\" />\n {{ opt.label }}\n </div>\n <p\n v-if=\"!options.length\"\n class=\"px-4 py-3 text-center text-body-small text-on-surface-variant\"\n >\n Sin opciones\n </p>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, onMounted, onUnmounted } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number | null\n options: { label: string; value: string | number; disabled?: boolean }[]\n label?: string\n placeholder?: string\n variant?: 'filled' | 'outlined'\n disabled?: boolean\n error?: string\n hint?: string\n required?: boolean\n leadingIcon?: string\n fieldBg?: string\n }>(),\n {\n modelValue: null,\n variant: 'filled',\n disabled: false,\n required: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number] }>()\n\nconst id = useId()\nconst open = ref(false)\nconst fieldEl = ref<HTMLElement | null>(null)\nconst { resolvedFieldBg } = useFieldBg(fieldEl, () => props.fieldBg)\nconst dropdownEl = ref<HTMLElement | null>(null)\nconst dropPos = ref({ top: '0px', left: '0px', width: '0px' })\n\nconst hasValue = computed(() => props.modelValue !== null && props.modelValue !== '')\nconst selectedLabel = computed(\n () => props.options.find((o) => o.value === props.modelValue)?.label ?? '',\n)\n\nfunction computeDropPos() {\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const dropH = Math.min(240, props.options.length * 52 + 8)\n const openAbove = spaceBelow < dropH && rect.top > dropH\n dropPos.value = {\n top: openAbove ? `${rect.top - 4 - dropH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n }\n}\n\nfunction toggle() {\n if (props.disabled) return\n if (!open.value) computeDropPos()\n open.value = !open.value\n}\n\nfunction select(opt: { value: string | number; disabled?: boolean }) {\n if (opt.disabled) return\n emit('update:modelValue', opt.value)\n open.value = false\n}\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (!fieldEl.value?.contains(t) && !dropdownEl.value?.contains(t)) open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n // Scrolling inside the dropdown list itself — do nothing\n if (dropdownEl.value?.contains(e.target as Node)) return\n // Recompute position to track the trigger element as the page scrolls\n if (!fieldEl.value) return\n const rect = fieldEl.value.getBoundingClientRect()\n // Only close if the trigger has scrolled completely out of the viewport\n if (rect.bottom < 0 || rect.top > window.innerHeight) {\n open.value = false\n return\n }\n computeDropPos()\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') { open.value = false; return }\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); return }\n if (!open.value) return\n const opts = props.options.filter((o) => !o.disabled)\n const idx = opts.findIndex((o) => o.value === props.modelValue)\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = opts[(idx + 1) % opts.length]\n if (next) emit('update:modelValue', next.value)\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n const prev = opts[(idx - 1 + opts.length) % opts.length]\n if (prev) emit('update:modelValue', prev.value)\n }\n}\n\nonMounted(() => {\n document.addEventListener('mousedown', onOutsideClick)\n window.addEventListener('scroll', onScroll, true)\n})\nonUnmounted(() => {\n document.removeEventListener('mousedown', onOutsideClick)\n window.removeEventListener('scroll', onScroll, true)\n})\n\nconst triggerClasses = computed(() => {\n const pl = props.leadingIcon ? 'pl-12' : 'pl-4'\n const base = [\n 'flex w-full cursor-pointer items-center pr-10 text-body-large transition-[border-color,border-width] duration-150',\n pl,\n ]\n\n if (props.variant === 'outlined') {\n return [\n ...base,\n 'h-14 rounded-sm border bg-transparent',\n open.value\n ? (props.error ? 'border-2 border-error' : 'border-2 border-primary')\n : (props.error ? 'border-error' : 'border-outline hover:border-on-surface'),\n ].join(' ')\n }\n\n return [\n ...base,\n 'h-14 rounded-t-sm bg-surface-container-highest border-b pt-6 pb-2',\n open.value\n ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')\n : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),\n ].join(' ')\n})\n\nconst isFloated = computed(() => hasValue.value || open.value)\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? (props.variant === 'outlined' ? 'left-11' : 'left-12')\n : (props.variant === 'outlined' ? 'left-3' : 'left-4')\n\n const floated = props.variant === 'outlined'\n ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'\n : 'top-2 translate-y-0 text-label-small'\n\n const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'\n\n return [\n 'pointer-events-none absolute right-10 truncate transition-all duration-200',\n left,\n isFloated.value ? floated : unFloated,\n open.value\n ? (props.error ? 'text-error' : 'text-primary')\n : (props.error ? 'text-error' : 'text-on-surface-variant'),\n ].join(' ')\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div\n ref=\"fieldEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <!-- Leading icon -->\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <!-- Custom trigger -->\n <div\n :id=\"id\"\n :tabindex=\"disabled ? -1 : 0\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled\"\n :class=\"[triggerClasses, disabled ? 'pointer-events-none opacity-[0.38]' : '']\"\n @click=\"toggle\"\n @keydown=\"onKeydown\"\n >\n <span v-if=\"hasValue\" class=\"text-on-surface\">{{ selectedLabel }}</span>\n </div>\n\n <!-- Floating label -->\n <label :for=\"id\" :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <!-- Arrow icon -->\n <div class=\"pointer-events-none absolute right-2 top-1/2 -translate-y-1/2\">\n <MIcon\n :name=\"open ? 'arrow_drop_up' : 'arrow_drop_down'\"\n :size=\"24\"\n class=\"text-on-surface-variant transition-transform duration-200\"\n />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n\n <!-- Dropdown teleported to body to escape overflow clipping -->\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n enter-to-class=\"opacity-100 translate-y-0 scale-100\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-from-class=\"opacity-100 translate-y-0 scale-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"dropdownEl\"\n class=\"fixed z-[500] max-h-60 overflow-auto rounded-sm bg-surface-container py-1 shadow-elevation-2\"\n :style=\"dropPos\"\n >\n <div\n v-for=\"opt in options\"\n :key=\"opt.value\"\n class=\"flex cursor-pointer items-center gap-3 px-4 py-3 text-body-large\"\n :class=\"[\n opt.disabled\n ? 'cursor-not-allowed opacity-38 text-on-surface'\n : 'text-on-surface hover:bg-on-surface/8',\n opt.value === modelValue ? 'bg-primary/8 text-primary font-medium' : '',\n ]\"\n @click=\"select(opt)\"\n >\n <MIcon\n v-if=\"opt.value === modelValue\"\n name=\"check\"\n :size=\"18\"\n class=\"shrink-0 text-primary\"\n />\n <span v-else class=\"w-[18px] shrink-0\" />\n {{ opt.label }}\n </div>\n <p\n v-if=\"!options.length\"\n class=\"px-4 py-3 text-center text-body-small text-on-surface-variant\"\n >\n Sin opciones\n </p>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n modelValue: boolean\n title?: string\n width?: string\n}>(), { width: 'w-80' })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\nconst close = () => emit('update:modelValue', false)\n\nconst dragX = ref(0)\nconst dragging = ref(false)\nlet startX = 0\n\nfunction onEdgePointerDown(e: PointerEvent) {\n dragging.value = true\n startX = e.clientX\n dragX.value = 0\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onEdgePointerMove(e: PointerEvent) {\n if (!dragging.value) return\n dragX.value = Math.max(0, e.clientX - startX)\n}\nfunction onEdgePointerUp() {\n if (dragX.value > 100) close()\n dragging.value = false\n dragX.value = 0\n}\n\nconst panelStyle = computed(() => ({\n transform: `translateX(${dragX.value}px)`,\n transition: dragging.value ? 'none' : undefined,\n}))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"ss\" :duration=\"{ enter: 320, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[200] flex justify-end\">\n <!-- Scrim -->\n <div class=\"ss-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <aside\n class=\"ss-panel relative flex h-full flex-col bg-surface-container-low shadow-elevation-3\"\n :class=\"[width, 'max-w-[90vw]']\"\n :style=\"panelStyle\"\n >\n <!-- Drag edge -->\n <div\n class=\"absolute top-0 left-0 h-full w-3 cursor-ew-resize touch-none\"\n @pointerdown=\"onEdgePointerDown\"\n @pointermove=\"onEdgePointerMove\"\n @pointerup=\"onEdgePointerUp\"\n />\n\n <!-- Header -->\n <div v-if=\"title || $slots.header\" class=\"flex shrink-0 items-center justify-between border-b border-outline-variant px-6 py-4\">\n <slot name=\"header\">\n <h2 class=\"text-title-large text-on-surface\">{{ title }}</h2>\n </slot>\n <button\n type=\"button\"\n class=\"flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"20\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"flex-1 overflow-y-auto px-6 py-4\">\n <slot />\n </div>\n\n <!-- Actions -->\n <div v-if=\"$slots.actions\" class=\"shrink-0 border-t border-outline-variant px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </aside>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.ss-scrim {\n transition: opacity 280ms ease;\n}\n.ss-enter-from .ss-scrim,\n.ss-leave-to .ss-scrim {\n opacity: 0;\n}\n\n.ss-panel {\n transition:\n transform 320ms cubic-bezier(0.2, 0, 0, 1),\n opacity 240ms ease;\n}\n.ss-enter-from .ss-panel {\n transform: translateX(40%);\n opacity: 0;\n}\n.ss-leave-to .ss-panel {\n transform: translateX(100%) !important;\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nwithDefaults(defineProps<{\n modelValue: boolean\n title?: string\n width?: string\n}>(), { width: 'w-80' })\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\nconst close = () => emit('update:modelValue', false)\n\nconst dragX = ref(0)\nconst dragging = ref(false)\nlet startX = 0\n\nfunction onEdgePointerDown(e: PointerEvent) {\n dragging.value = true\n startX = e.clientX\n dragX.value = 0\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n}\nfunction onEdgePointerMove(e: PointerEvent) {\n if (!dragging.value) return\n dragX.value = Math.max(0, e.clientX - startX)\n}\nfunction onEdgePointerUp() {\n if (dragX.value > 100) close()\n dragging.value = false\n dragX.value = 0\n}\n\nconst panelStyle = computed(() => ({\n transform: `translateX(${dragX.value}px)`,\n transition: dragging.value ? 'none' : undefined,\n}))\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"ss\" :duration=\"{ enter: 320, leave: 280 }\">\n <div v-if=\"modelValue\" class=\"fixed inset-0 z-[200] flex justify-end\">\n <!-- Scrim -->\n <div class=\"ss-scrim absolute inset-0 bg-black/40\" @click=\"close\" />\n\n <!-- Panel -->\n <aside\n class=\"ss-panel relative flex h-full flex-col bg-surface-container-low shadow-elevation-3\"\n :class=\"[width, 'max-w-[90vw]']\"\n :style=\"panelStyle\"\n >\n <!-- Drag edge -->\n <div\n class=\"absolute top-0 left-0 h-full w-3 cursor-ew-resize touch-none\"\n @pointerdown=\"onEdgePointerDown\"\n @pointermove=\"onEdgePointerMove\"\n @pointerup=\"onEdgePointerUp\"\n />\n\n <!-- Header -->\n <div v-if=\"title || $slots.header\" class=\"flex shrink-0 items-center justify-between border-b border-outline-variant px-6 py-4\">\n <slot name=\"header\">\n <h2 class=\"text-title-large text-on-surface\">{{ title }}</h2>\n </slot>\n <button\n type=\"button\"\n class=\"flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"20\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"flex-1 overflow-y-auto px-6 py-4\">\n <slot />\n </div>\n\n <!-- Actions -->\n <div v-if=\"$slots.actions\" class=\"shrink-0 border-t border-outline-variant px-6 py-4\">\n <slot name=\"actions\" />\n </div>\n </aside>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.ss-scrim {\n transition: opacity 280ms ease;\n}\n.ss-enter-from .ss-scrim,\n.ss-leave-to .ss-scrim {\n opacity: 0;\n}\n\n.ss-panel {\n transition:\n transform 320ms cubic-bezier(0.2, 0, 0, 1),\n opacity 240ms ease;\n}\n.ss-enter-from .ss-panel {\n transform: translateX(40%);\n opacity: 0;\n}\n.ss-leave-to .ss-panel {\n transform: translateX(100%) !important;\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n variant?: 'text' | 'circular' | 'rectangular' | 'rounded'\n width?: string\n height?: string\n lines?: number\n animation?: 'pulse' | 'wave' | 'none'\n}>(), {\n variant: 'text',\n animation: 'pulse',\n lines: 1,\n})\n</script>\n\n<template>\n <!-- Multi-line text skeleton -->\n <div v-if=\"variant === 'text' && lines > 1\" class=\"flex flex-col gap-2.5\">\n <div\n v-for=\"i in lines\"\n :key=\"i\"\n class=\"h-3.5 rounded-full bg-on-surface/10\"\n :class=\"animation === 'pulse' ? 'animate-pulse' : animation === 'wave' ? 'skeleton-wave' : ''\"\n :style=\"{ width: i === lines ? '60%' : (width ?? '100%') }\"\n />\n </div>\n\n <!-- Single element -->\n <div\n v-else\n class=\"bg-on-surface/10\"\n :class=\"[\n variant === 'circular' ? 'rounded-full' : variant === 'rounded' ? 'rounded-lg' : variant === 'text' ? 'rounded-full' : 'rounded-sm',\n animation === 'pulse' ? 'animate-pulse' : animation === 'wave' ? 'skeleton-wave' : '',\n ]\"\n :style=\"{\n width: width ?? (variant === 'circular' ? '40px' : '100%'),\n height: height ?? (variant === 'circular' ? '40px' : variant === 'text' ? '14px' : '100px'),\n }\"\n />\n</template>\n\n<style scoped>\n@keyframes skeleton-wave-move {\n 0% { transform: translateX(-100%); }\n 60% { transform: translateX(100%); }\n 100% { transform: translateX(100%); }\n}\n.skeleton-wave {\n position: relative;\n overflow: hidden;\n}\n.skeleton-wave::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent 0%, var(--color-on-surface) 50%, transparent 100%);\n opacity: 0.06;\n animation: skeleton-wave-move 1.8s ease-in-out infinite;\n}\n</style>\n","<script setup lang=\"ts\">\nwithDefaults(defineProps<{\n variant?: 'text' | 'circular' | 'rectangular' | 'rounded'\n width?: string\n height?: string\n lines?: number\n animation?: 'pulse' | 'wave' | 'none'\n}>(), {\n variant: 'text',\n animation: 'pulse',\n lines: 1,\n})\n</script>\n\n<template>\n <!-- Multi-line text skeleton -->\n <div v-if=\"variant === 'text' && lines > 1\" class=\"flex flex-col gap-2.5\">\n <div\n v-for=\"i in lines\"\n :key=\"i\"\n class=\"h-3.5 rounded-full bg-on-surface/10\"\n :class=\"animation === 'pulse' ? 'animate-pulse' : animation === 'wave' ? 'skeleton-wave' : ''\"\n :style=\"{ width: i === lines ? '60%' : (width ?? '100%') }\"\n />\n </div>\n\n <!-- Single element -->\n <div\n v-else\n class=\"bg-on-surface/10\"\n :class=\"[\n variant === 'circular' ? 'rounded-full' : variant === 'rounded' ? 'rounded-lg' : variant === 'text' ? 'rounded-full' : 'rounded-sm',\n animation === 'pulse' ? 'animate-pulse' : animation === 'wave' ? 'skeleton-wave' : '',\n ]\"\n :style=\"{\n width: width ?? (variant === 'circular' ? '40px' : '100%'),\n height: height ?? (variant === 'circular' ? '40px' : variant === 'text' ? '14px' : '100px'),\n }\"\n />\n</template>\n\n<style scoped>\n@keyframes skeleton-wave-move {\n 0% { transform: translateX(-100%); }\n 60% { transform: translateX(100%); }\n 100% { transform: translateX(100%); }\n}\n.skeleton-wave {\n position: relative;\n overflow: hidden;\n}\n.skeleton-wave::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent 0%, var(--color-on-surface) 50%, transparent 100%);\n opacity: 0.06;\n animation: skeleton-wave-move 1.8s ease-in-out infinite;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n step?: number;\n disabled?: boolean;\n label?: string;\n showValue?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n }>(),\n {\n min: 0,\n max: 100,\n step: 1,\n disabled: false,\n showValue: false,\n color: \"primary\",\n },\n);\n\nconst emit = defineEmits<{\n \"update:modelValue\": [number];\n}>();\n\nconst trackEl = ref<HTMLElement>();\nconst dragging = ref(false);\n\nconst pct = computed(() => {\n const range = props.max - props.min;\n return range === 0 ? 0 : ((props.modelValue - props.min) / range) * 100;\n});\n\nconst colors: Record<string, { active: string; inactive: string; thumb: string }> = {\n primary: {\n active: \"bg-primary\",\n inactive: \"bg-primary-container\",\n thumb: \"bg-primary\",\n },\n secondary: {\n active: \"bg-secondary\",\n inactive: \"bg-secondary-container\",\n thumb: \"bg-secondary\",\n },\n tertiary: {\n active: \"bg-tertiary\",\n inactive: \"bg-tertiary-container\",\n thumb: \"bg-tertiary\",\n },\n error: {\n active: \"bg-error\",\n inactive: \"bg-error-container\",\n thumb: \"bg-error\",\n },\n};\n\nfunction clamp(v: number) {\n const stepped = Math.round((v - props.min) / props.step) * props.step + props.min;\n\n return Math.max(props.min, Math.min(props.max, stepped));\n}\n\nfunction valueFromX(clientX: number) {\n if (!trackEl.value) return props.modelValue;\n\n const rect = trackEl.value.getBoundingClientRect();\n\n const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n\n return clamp(props.min + ratio * (props.max - props.min));\n}\n\nfunction onPointerDown(e: PointerEvent) {\n if (props.disabled) return;\n\n e.preventDefault();\n\n dragging.value = true;\n\n emit(\"update:modelValue\", valueFromX(e.clientX));\n\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerUp);\n}\n\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value) return;\n\n emit(\"update:modelValue\", valueFromX(e.clientX));\n}\n\nfunction onPointerUp() {\n dragging.value = false;\n\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n}\n\nfunction onKeyDown(e: KeyboardEvent) {\n if (props.disabled) return;\n\n const delta = {\n ArrowRight: 1,\n ArrowUp: 1,\n ArrowLeft: -1,\n ArrowDown: -1,\n }[e.key];\n\n if (delta !== undefined) {\n e.preventDefault();\n\n emit(\"update:modelValue\", clamp(props.modelValue + delta * props.step));\n }\n\n if (e.key === \"Home\") {\n e.preventDefault();\n emit(\"update:modelValue\", props.min);\n }\n\n if (e.key === \"End\") {\n e.preventDefault();\n emit(\"update:modelValue\", props.max);\n }\n}\n\nonBeforeUnmount(() => {\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n});\n\nconst thumbStyle = computed(() => ({\n left: `${pct.value}%`,\n top: \"50%\",\n transform: `translateX(-50%) translateY(-50%) scale(${dragging.value ? 1.15 : 1})`,\n transition: dragging.value ? \"transform 80ms ease\" : \"left 75ms ease, transform 80ms ease\",\n}));\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1 select-none\">\n <div v-if=\"label || showValue\" class=\"flex items-center justify-between\">\n <span v-if=\"label\" class=\"text-label-large text-on-surface\">\n {{ label }}\n </span>\n\n <span v-if=\"showValue\" class=\"tabular-nums text-label-large text-on-surface-variant\">\n {{ modelValue }}\n </span>\n </div>\n\n <div\n ref=\"trackEl\"\n role=\"slider\"\n tabindex=\"0\"\n :aria-valuenow=\"modelValue\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-disabled=\"disabled || undefined\"\n class=\"relative flex h-10 w-full touch-none cursor-pointer items-center outline-none\"\n :class=\"disabled && 'cursor-not-allowed opacity-[0.38]'\"\n @pointerdown=\"onPointerDown\"\n @keydown=\"onKeyDown\"\n >\n <div class=\"relative h-1 w-full rounded-full\" :class=\"colors[color]!.inactive\">\n <div\n class=\"h-full rounded-full\"\n :class=\"colors[color]!.active\"\n :style=\"{\n width: `${pct}%`,\n transition: dragging ? 'none' : 'width 75ms ease',\n }\"\n />\n </div>\n\n <div\n class=\"pointer-events-none absolute h-5 w-5 rounded-full shadow-elevation-1\"\n :class=\"colors[color]!.thumb\"\n :style=\"thumbStyle\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n step?: number;\n disabled?: boolean;\n label?: string;\n showValue?: boolean;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\";\n }>(),\n {\n min: 0,\n max: 100,\n step: 1,\n disabled: false,\n showValue: false,\n color: \"primary\",\n },\n);\n\nconst emit = defineEmits<{\n \"update:modelValue\": [number];\n}>();\n\nconst trackEl = ref<HTMLElement>();\nconst dragging = ref(false);\n\nconst pct = computed(() => {\n const range = props.max - props.min;\n return range === 0 ? 0 : ((props.modelValue - props.min) / range) * 100;\n});\n\nconst colors: Record<string, { active: string; inactive: string; thumb: string }> = {\n primary: {\n active: \"bg-primary\",\n inactive: \"bg-primary-container\",\n thumb: \"bg-primary\",\n },\n secondary: {\n active: \"bg-secondary\",\n inactive: \"bg-secondary-container\",\n thumb: \"bg-secondary\",\n },\n tertiary: {\n active: \"bg-tertiary\",\n inactive: \"bg-tertiary-container\",\n thumb: \"bg-tertiary\",\n },\n error: {\n active: \"bg-error\",\n inactive: \"bg-error-container\",\n thumb: \"bg-error\",\n },\n};\n\nfunction clamp(v: number) {\n const stepped = Math.round((v - props.min) / props.step) * props.step + props.min;\n\n return Math.max(props.min, Math.min(props.max, stepped));\n}\n\nfunction valueFromX(clientX: number) {\n if (!trackEl.value) return props.modelValue;\n\n const rect = trackEl.value.getBoundingClientRect();\n\n const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n\n return clamp(props.min + ratio * (props.max - props.min));\n}\n\nfunction onPointerDown(e: PointerEvent) {\n if (props.disabled) return;\n\n e.preventDefault();\n\n dragging.value = true;\n\n emit(\"update:modelValue\", valueFromX(e.clientX));\n\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerUp);\n}\n\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value) return;\n\n emit(\"update:modelValue\", valueFromX(e.clientX));\n}\n\nfunction onPointerUp() {\n dragging.value = false;\n\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n}\n\nfunction onKeyDown(e: KeyboardEvent) {\n if (props.disabled) return;\n\n const delta = {\n ArrowRight: 1,\n ArrowUp: 1,\n ArrowLeft: -1,\n ArrowDown: -1,\n }[e.key];\n\n if (delta !== undefined) {\n e.preventDefault();\n\n emit(\"update:modelValue\", clamp(props.modelValue + delta * props.step));\n }\n\n if (e.key === \"Home\") {\n e.preventDefault();\n emit(\"update:modelValue\", props.min);\n }\n\n if (e.key === \"End\") {\n e.preventDefault();\n emit(\"update:modelValue\", props.max);\n }\n}\n\nonBeforeUnmount(() => {\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n});\n\nconst thumbStyle = computed(() => ({\n left: `${pct.value}%`,\n top: \"50%\",\n transform: `translateX(-50%) translateY(-50%) scale(${dragging.value ? 1.15 : 1})`,\n transition: dragging.value ? \"transform 80ms ease\" : \"left 75ms ease, transform 80ms ease\",\n}));\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1 select-none\">\n <div v-if=\"label || showValue\" class=\"flex items-center justify-between\">\n <span v-if=\"label\" class=\"text-label-large text-on-surface\">\n {{ label }}\n </span>\n\n <span v-if=\"showValue\" class=\"tabular-nums text-label-large text-on-surface-variant\">\n {{ modelValue }}\n </span>\n </div>\n\n <div\n ref=\"trackEl\"\n role=\"slider\"\n tabindex=\"0\"\n :aria-valuenow=\"modelValue\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-disabled=\"disabled || undefined\"\n class=\"relative flex h-10 w-full touch-none cursor-pointer items-center outline-none\"\n :class=\"disabled && 'cursor-not-allowed opacity-[0.38]'\"\n @pointerdown=\"onPointerDown\"\n @keydown=\"onKeyDown\"\n >\n <div class=\"relative h-1 w-full rounded-full\" :class=\"colors[color]!.inactive\">\n <div\n class=\"h-full rounded-full\"\n :class=\"colors[color]!.active\"\n :style=\"{\n width: `${pct}%`,\n transition: dragging ? 'none' : 'width 75ms ease',\n }\"\n />\n </div>\n\n <div\n class=\"pointer-events-none absolute h-5 w-5 rounded-full shadow-elevation-1\"\n :class=\"colors[color]!.thumb\"\n :style=\"thumbStyle\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useToast } from \"../composables/useToast\";\nimport MIcon from \"./MIcon.vue\";\n\nconst { toasts, position, dismiss } = useToast();\n\nconst isTop = computed(() => position.value.startsWith(\"top\"));\n\nconst containerClass = computed(() => {\n const base = \"pointer-events-none fixed z-[300] flex flex-col\";\n switch (position.value) {\n case \"top-left\":\n return `${base} top-4 left-4 items-start`;\n case \"top-center\":\n return `${base} top-4 left-1/2 -translate-x-1/2 items-center`;\n case \"top-right\":\n return `${base} top-4 right-4 items-end`;\n case \"bottom-left\":\n return `${base} bottom-4 left-4 items-start`;\n case \"bottom-right\":\n return `${base} bottom-4 right-4 items-end`;\n default:\n return `${base} bottom-4 left-1/2 -translate-x-1/2 items-center`;\n }\n});\n\ntype VariantStyle = {\n container: string;\n icon: string;\n iconName: string;\n action: string;\n close: string;\n progress: string;\n};\n\nconst variantStyles: Record<string, VariantStyle> = {\n info: {\n // secondary-container tokens are adaptive light/dark out of the box\n container:\n \"bg-secondary-container text-on-secondary-container ring-1 ring-inset ring-on-secondary-container/8\",\n icon: \"text-on-secondary-container/70\",\n iconName: \"info\",\n action: \"text-on-secondary-container hover:bg-on-secondary-container/10\",\n close: \"text-on-secondary-container/60 hover:bg-on-secondary-container/10\",\n progress: \"bg-on-secondary-container/25\",\n },\n success: {\n container:\n \"bg-[#dcfce7] text-[#14532d] ring-1 ring-inset ring-[#14532d]/10 dark:bg-[#052e16] dark:text-[#bbf7d0] dark:ring-white/8\",\n icon: \"text-[#16a34a] dark:text-[#4ade80]\",\n iconName: \"check_circle\",\n action: \"text-[#166534] hover:bg-[#14532d]/10 dark:text-[#86efac] dark:hover:bg-white/10\",\n close: \"text-[#14532d]/50 hover:bg-[#14532d]/10 dark:text-[#bbf7d0]/50 dark:hover:bg-white/10\",\n progress: \"bg-[#16a34a]/35 dark:bg-[#4ade80]/30\",\n },\n warning: {\n container:\n \"bg-[#fefce8] text-[#713f12] ring-1 ring-inset ring-[#713f12]/10 dark:bg-[#2d1a00] dark:text-[#fde68a] dark:ring-white/8\",\n icon: \"text-[#d97706] dark:text-[#fcd34d]\",\n iconName: \"warning\",\n action: \"text-[#92400e] hover:bg-[#713f12]/10 dark:text-[#fcd34d] dark:hover:bg-white/10\",\n close: \"text-[#713f12]/50 hover:bg-[#713f12]/10 dark:text-[#fde68a]/50 dark:hover:bg-white/10\",\n progress: \"bg-[#d97706]/35 dark:bg-[#fcd34d]/30\",\n },\n error: {\n // error-container tokens are adaptive light/dark out of the box\n container:\n \"bg-error-container text-on-error-container ring-1 ring-inset ring-on-error-container/8\",\n icon: \"text-error dark:text-[#fca5a5]\",\n iconName: \"error\",\n action: \"text-on-error-container hover:bg-on-error-container/10\",\n close: \"text-on-error-container/60 hover:bg-on-error-container/10\",\n progress: \"bg-on-error-container/25\",\n },\n};\n\nconst getVariantStyle = (variant: string): VariantStyle =>\n variantStyles[variant] ?? variantStyles.info!;\n</script>\n\n<template>\n <div :class=\"containerClass\">\n <TransitionGroup :name=\"isTop ? 'm3-toast-top' : 'm3-toast-bot'\">\n <div v-for=\"t in toasts\" :key=\"t.id\" class=\"toast-row w-full min-w-64 max-w-xs\">\n <div\n class=\"toast-inner pointer-events-auto relative flex items-center gap-3 overflow-hidden rounded-2xl px-4 py-4 shadow-elevation-2\"\n :class=\"getVariantStyle(t.variant).container\"\n >\n <MIcon\n :name=\"getVariantStyle(t.variant).iconName\"\n :size=\"20\"\n class=\"shrink-0\"\n :class=\"getVariantStyle(t.variant).icon\"\n />\n\n <p class=\"flex-1 text-body-medium leading-snug\">{{ t.message }}</p>\n\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n v-if=\"t.action\"\n type=\"button\"\n class=\"cursor-pointer rounded px-2 py-1 text-label-medium font-semibold transition-colors\"\n :class=\"getVariantStyle(t.variant).action\"\n @click=\"\n () => {\n t.action!.onClick();\n dismiss(t.id);\n }\n \"\n >\n {{ t.action.label }}\n </button>\n\n <button\n type=\"button\"\n class=\"flex h-8 w-8 cursor-pointer items-center justify-center rounded-full transition-colors\"\n :class=\"getVariantStyle(t.variant).close\"\n aria-label=\"Cerrar\"\n @click=\"dismiss(t.id)\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <!-- Countdown progress bar -->\n <div\n v-if=\"t.duration > 0\"\n class=\"absolute right-0 bottom-0 left-0 h-0.5 origin-left\"\n :class=\"getVariantStyle(t.variant).progress\"\n :style=\"{ animation: `m3-toast-progress ${t.duration}ms linear forwards` }\"\n />\n </div>\n </div>\n </TransitionGroup>\n </div>\n</template>\n\n<style scoped>\n/*\n .toast-row is a grid container — animating grid-template-rows: 1fr → 0fr\n collapses height smoothly without position:absolute, so sibling toasts\n shift up gracefully instead of jumping.\n*/\n.toast-row {\n display: grid;\n grid-template-rows: 1fr;\n padding-bottom: 8px;\n}\n.toast-row > .toast-inner {\n min-height: 0; /* required for 0fr collapse */\n}\n\n/* ─── Bottom toasts ─────────────────────────────────────────────── */\n.m3-toast-bot-enter-active {\n transition:\n grid-template-rows 220ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 220ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-bot-enter-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 220ms cubic-bezier(0.2, 0, 0, 1);\n}\n.m3-toast-bot-enter-from {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-bot-enter-from > .toast-inner {\n opacity: 0;\n transform: translateY(20px) scale(0.94);\n}\n\n.m3-toast-bot-leave-active {\n transition:\n grid-template-rows 300ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 300ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-bot-leave-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 180ms ease;\n}\n.m3-toast-bot-leave-to {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-bot-leave-to > .toast-inner {\n opacity: 0;\n transform: scale(0.92);\n}\n\n/* ─── Top toasts ────────────────────────────────────────────────── */\n.m3-toast-top-enter-active {\n transition:\n grid-template-rows 220ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 220ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-top-enter-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 220ms cubic-bezier(0.2, 0, 0, 1);\n}\n.m3-toast-top-enter-from {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-top-enter-from > .toast-inner {\n opacity: 0;\n transform: translateY(-20px) scale(0.94);\n}\n\n.m3-toast-top-leave-active {\n transition:\n grid-template-rows 300ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 300ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-top-leave-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 180ms ease;\n}\n.m3-toast-top-leave-to {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-top-leave-to > .toast-inner {\n opacity: 0;\n transform: scale(0.92);\n}\n\n@keyframes m3-toast-progress {\n from {\n transform: scaleX(1);\n }\n to {\n transform: scaleX(0);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useToast } from \"../composables/useToast\";\nimport MIcon from \"./MIcon.vue\";\n\nconst { toasts, position, dismiss } = useToast();\n\nconst isTop = computed(() => position.value.startsWith(\"top\"));\n\nconst containerClass = computed(() => {\n const base = \"pointer-events-none fixed z-[300] flex flex-col\";\n switch (position.value) {\n case \"top-left\":\n return `${base} top-4 left-4 items-start`;\n case \"top-center\":\n return `${base} top-4 left-1/2 -translate-x-1/2 items-center`;\n case \"top-right\":\n return `${base} top-4 right-4 items-end`;\n case \"bottom-left\":\n return `${base} bottom-4 left-4 items-start`;\n case \"bottom-right\":\n return `${base} bottom-4 right-4 items-end`;\n default:\n return `${base} bottom-4 left-1/2 -translate-x-1/2 items-center`;\n }\n});\n\ntype VariantStyle = {\n container: string;\n icon: string;\n iconName: string;\n action: string;\n close: string;\n progress: string;\n};\n\nconst variantStyles: Record<string, VariantStyle> = {\n info: {\n // secondary-container tokens are adaptive light/dark out of the box\n container:\n \"bg-secondary-container text-on-secondary-container ring-1 ring-inset ring-on-secondary-container/8\",\n icon: \"text-on-secondary-container/70\",\n iconName: \"info\",\n action: \"text-on-secondary-container hover:bg-on-secondary-container/10\",\n close: \"text-on-secondary-container/60 hover:bg-on-secondary-container/10\",\n progress: \"bg-on-secondary-container/25\",\n },\n success: {\n container:\n \"bg-[#dcfce7] text-[#14532d] ring-1 ring-inset ring-[#14532d]/10 dark:bg-[#052e16] dark:text-[#bbf7d0] dark:ring-white/8\",\n icon: \"text-[#16a34a] dark:text-[#4ade80]\",\n iconName: \"check_circle\",\n action: \"text-[#166534] hover:bg-[#14532d]/10 dark:text-[#86efac] dark:hover:bg-white/10\",\n close: \"text-[#14532d]/50 hover:bg-[#14532d]/10 dark:text-[#bbf7d0]/50 dark:hover:bg-white/10\",\n progress: \"bg-[#16a34a]/35 dark:bg-[#4ade80]/30\",\n },\n warning: {\n container:\n \"bg-[#fefce8] text-[#713f12] ring-1 ring-inset ring-[#713f12]/10 dark:bg-[#2d1a00] dark:text-[#fde68a] dark:ring-white/8\",\n icon: \"text-[#d97706] dark:text-[#fcd34d]\",\n iconName: \"warning\",\n action: \"text-[#92400e] hover:bg-[#713f12]/10 dark:text-[#fcd34d] dark:hover:bg-white/10\",\n close: \"text-[#713f12]/50 hover:bg-[#713f12]/10 dark:text-[#fde68a]/50 dark:hover:bg-white/10\",\n progress: \"bg-[#d97706]/35 dark:bg-[#fcd34d]/30\",\n },\n error: {\n // error-container tokens are adaptive light/dark out of the box\n container:\n \"bg-error-container text-on-error-container ring-1 ring-inset ring-on-error-container/8\",\n icon: \"text-error dark:text-[#fca5a5]\",\n iconName: \"error\",\n action: \"text-on-error-container hover:bg-on-error-container/10\",\n close: \"text-on-error-container/60 hover:bg-on-error-container/10\",\n progress: \"bg-on-error-container/25\",\n },\n};\n\nconst getVariantStyle = (variant: string): VariantStyle =>\n variantStyles[variant] ?? variantStyles.info!;\n</script>\n\n<template>\n <div :class=\"containerClass\">\n <TransitionGroup :name=\"isTop ? 'm3-toast-top' : 'm3-toast-bot'\">\n <div v-for=\"t in toasts\" :key=\"t.id\" class=\"toast-row w-full min-w-64 max-w-xs\">\n <div\n class=\"toast-inner pointer-events-auto relative flex items-center gap-3 overflow-hidden rounded-2xl px-4 py-4 shadow-elevation-2\"\n :class=\"getVariantStyle(t.variant).container\"\n >\n <MIcon\n :name=\"getVariantStyle(t.variant).iconName\"\n :size=\"20\"\n class=\"shrink-0\"\n :class=\"getVariantStyle(t.variant).icon\"\n />\n\n <p class=\"flex-1 text-body-medium leading-snug\">{{ t.message }}</p>\n\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n v-if=\"t.action\"\n type=\"button\"\n class=\"cursor-pointer rounded px-2 py-1 text-label-medium font-semibold transition-colors\"\n :class=\"getVariantStyle(t.variant).action\"\n @click=\"\n () => {\n t.action!.onClick();\n dismiss(t.id);\n }\n \"\n >\n {{ t.action.label }}\n </button>\n\n <button\n type=\"button\"\n class=\"flex h-8 w-8 cursor-pointer items-center justify-center rounded-full transition-colors\"\n :class=\"getVariantStyle(t.variant).close\"\n aria-label=\"Cerrar\"\n @click=\"dismiss(t.id)\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <!-- Countdown progress bar -->\n <div\n v-if=\"t.duration > 0\"\n class=\"absolute right-0 bottom-0 left-0 h-0.5 origin-left\"\n :class=\"getVariantStyle(t.variant).progress\"\n :style=\"{ animation: `m3-toast-progress ${t.duration}ms linear forwards` }\"\n />\n </div>\n </div>\n </TransitionGroup>\n </div>\n</template>\n\n<style scoped>\n/*\n .toast-row is a grid container — animating grid-template-rows: 1fr → 0fr\n collapses height smoothly without position:absolute, so sibling toasts\n shift up gracefully instead of jumping.\n*/\n.toast-row {\n display: grid;\n grid-template-rows: 1fr;\n padding-bottom: 8px;\n}\n.toast-row > .toast-inner {\n min-height: 0; /* required for 0fr collapse */\n}\n\n/* ─── Bottom toasts ─────────────────────────────────────────────── */\n.m3-toast-bot-enter-active {\n transition:\n grid-template-rows 220ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 220ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-bot-enter-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 220ms cubic-bezier(0.2, 0, 0, 1);\n}\n.m3-toast-bot-enter-from {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-bot-enter-from > .toast-inner {\n opacity: 0;\n transform: translateY(20px) scale(0.94);\n}\n\n.m3-toast-bot-leave-active {\n transition:\n grid-template-rows 300ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 300ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-bot-leave-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 180ms ease;\n}\n.m3-toast-bot-leave-to {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-bot-leave-to > .toast-inner {\n opacity: 0;\n transform: scale(0.92);\n}\n\n/* ─── Top toasts ────────────────────────────────────────────────── */\n.m3-toast-top-enter-active {\n transition:\n grid-template-rows 220ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 220ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-top-enter-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 220ms cubic-bezier(0.2, 0, 0, 1);\n}\n.m3-toast-top-enter-from {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-top-enter-from > .toast-inner {\n opacity: 0;\n transform: translateY(-20px) scale(0.94);\n}\n\n.m3-toast-top-leave-active {\n transition:\n grid-template-rows 300ms cubic-bezier(0.2, 0, 0, 1),\n padding-bottom 300ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.m3-toast-top-leave-active > .toast-inner {\n transition:\n opacity 180ms ease,\n transform 180ms ease;\n}\n.m3-toast-top-leave-to {\n grid-template-rows: 0fr;\n padding-bottom: 0;\n}\n.m3-toast-top-leave-to > .toast-inner {\n opacity: 0;\n transform: scale(0.92);\n}\n\n@keyframes m3-toast-progress {\n from {\n transform: scaleX(1);\n }\n to {\n transform: scaleX(0);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n direction?: 'horizontal' | 'vertical'\n initialSplit?: number\n min?: number\n max?: number\n }>(),\n { direction: 'horizontal', initialSplit: 50, min: 10, max: 90 },\n)\n\nconst split = ref(props.initialSplit)\nconst dragging = ref(false)\nconst containerRef = ref<HTMLElement | null>(null)\n\nconst isHorizontal = computed(() => props.direction === 'horizontal')\n\nconst panelAStyle = computed(() =>\n isHorizontal.value\n ? { width: `${split.value}%` }\n : { height: `${split.value}%` },\n)\n\nconst panelBStyle = computed(() =>\n isHorizontal.value\n ? { width: `${100 - split.value}%` }\n : { height: `${100 - split.value}%` },\n)\n\nfunction onPointerDown(e: PointerEvent) {\n dragging.value = true\n ;(e.target as HTMLElement).setPointerCapture(e.pointerId)\n}\n\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value || !containerRef.value) return\n\n const rect = containerRef.value.getBoundingClientRect()\n let pct: number\n\n if (isHorizontal.value) {\n pct = ((e.clientX - rect.left) / rect.width) * 100\n } else {\n pct = ((e.clientY - rect.top) / rect.height) * 100\n }\n\n split.value = Math.min(props.max, Math.max(props.min, pct))\n}\n\nfunction onPointerUp() {\n dragging.value = false\n}\n\nonBeforeUnmount(() => {\n dragging.value = false\n})\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"flex overflow-hidden\"\n :class=\"[\n isHorizontal ? 'flex-row' : 'flex-col',\n dragging && 'select-none',\n ]\"\n style=\"height: 100%\"\n >\n <div class=\"overflow-auto\" :style=\"panelAStyle\">\n <slot name=\"first\" />\n </div>\n\n <div\n class=\"z-10 flex shrink-0 items-center justify-center transition-colors\"\n :class=\"[\n isHorizontal\n ? 'w-2 cursor-col-resize flex-col'\n : 'h-2 cursor-row-resize flex-row',\n dragging ? 'bg-primary/20' : 'bg-outline-variant/40 hover:bg-primary/12',\n ]\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n >\n <div\n class=\"rounded-full bg-outline\"\n :class=\"isHorizontal ? 'h-6 w-1' : 'h-1 w-6'\"\n />\n </div>\n\n <div class=\"overflow-auto\" :style=\"panelBStyle\">\n <slot name=\"second\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n direction?: 'horizontal' | 'vertical'\n initialSplit?: number\n min?: number\n max?: number\n }>(),\n { direction: 'horizontal', initialSplit: 50, min: 10, max: 90 },\n)\n\nconst split = ref(props.initialSplit)\nconst dragging = ref(false)\nconst containerRef = ref<HTMLElement | null>(null)\n\nconst isHorizontal = computed(() => props.direction === 'horizontal')\n\nconst panelAStyle = computed(() =>\n isHorizontal.value\n ? { width: `${split.value}%` }\n : { height: `${split.value}%` },\n)\n\nconst panelBStyle = computed(() =>\n isHorizontal.value\n ? { width: `${100 - split.value}%` }\n : { height: `${100 - split.value}%` },\n)\n\nfunction onPointerDown(e: PointerEvent) {\n dragging.value = true\n ;(e.target as HTMLElement).setPointerCapture(e.pointerId)\n}\n\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value || !containerRef.value) return\n\n const rect = containerRef.value.getBoundingClientRect()\n let pct: number\n\n if (isHorizontal.value) {\n pct = ((e.clientX - rect.left) / rect.width) * 100\n } else {\n pct = ((e.clientY - rect.top) / rect.height) * 100\n }\n\n split.value = Math.min(props.max, Math.max(props.min, pct))\n}\n\nfunction onPointerUp() {\n dragging.value = false\n}\n\nonBeforeUnmount(() => {\n dragging.value = false\n})\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"flex overflow-hidden\"\n :class=\"[\n isHorizontal ? 'flex-row' : 'flex-col',\n dragging && 'select-none',\n ]\"\n style=\"height: 100%\"\n >\n <div class=\"overflow-auto\" :style=\"panelAStyle\">\n <slot name=\"first\" />\n </div>\n\n <div\n class=\"z-10 flex shrink-0 items-center justify-center transition-colors\"\n :class=\"[\n isHorizontal\n ? 'w-2 cursor-col-resize flex-col'\n : 'h-2 cursor-row-resize flex-row',\n dragging ? 'bg-primary/20' : 'bg-outline-variant/40 hover:bg-primary/12',\n ]\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n >\n <div\n class=\"rounded-full bg-outline\"\n :class=\"isHorizontal ? 'h-6 w-1' : 'h-1 w-6'\"\n />\n </div>\n\n <div class=\"overflow-auto\" :style=\"panelBStyle\">\n <slot name=\"second\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MSpinner from './MSpinner.vue'\n\nexport interface SpotlightResult {\n id: string | number\n title: string\n description?: string\n icon?: string\n category?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n results?: SpotlightResult[]\n placeholder?: string\n loading?: boolean\n noResultsText?: string\n hotkey?: string\n debounce?: number\n }>(),\n {\n results: () => [],\n placeholder: 'Buscar...',\n loading: false,\n noResultsText: 'No se encontraron resultados',\n hotkey: '/',\n debounce: 0,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n search: [string]\n select: [SpotlightResult]\n}>()\n\nconst query = ref('')\nconst activeIndex = ref(0)\nconst inputRef = ref<HTMLInputElement | null>(null)\nlet debounceTimer: ReturnType<typeof setTimeout> | null = null\n\nconst hasQuery = computed(() => query.value.trim().length > 0)\n\nconst grouped = computed(() => {\n const map = new Map<string, SpotlightResult[]>()\n for (const r of props.results) {\n const cat = r.category ?? ''\n if (!map.has(cat)) map.set(cat, [])\n map.get(cat)!.push(r)\n }\n return map\n})\n\nfunction close() {\n query.value = ''\n activeIndex.value = 0\n emit('update:modelValue', false)\n}\n\nfunction selectResult(result: SpotlightResult) {\n emit('select', result)\n close()\n}\n\nfunction emitSearch() {\n if (debounceTimer) clearTimeout(debounceTimer)\n if (props.debounce > 0) {\n debounceTimer = setTimeout(() => emit('search', query.value), props.debounce)\n } else {\n emit('search', query.value)\n }\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n const len = props.results.length\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n activeIndex.value = len ? (activeIndex.value + 1) % len : 0\n scrollToActive()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n activeIndex.value = len ? (activeIndex.value - 1 + len) % len : 0\n scrollToActive()\n } else if (e.key === 'Enter' && len) {\n e.preventDefault()\n selectResult(props.results[activeIndex.value]!)\n } else if (e.key === 'Escape') {\n close()\n }\n}\n\nfunction scrollToActive() {\n nextTick(() => {\n const el = document.querySelector('[data-spot-active=\"true\"]')\n el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction onGlobalKeydown(e: KeyboardEvent) {\n const tag = (e.target as HTMLElement).tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable) return\n if (e.key === props.hotkey && !e.metaKey && !e.ctrlKey && !e.altKey) {\n e.preventDefault()\n emit('update:modelValue', true)\n }\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.body.style.overflow = 'hidden'\n nextTick(() => inputRef.value?.focus())\n } else {\n document.body.style.overflow = ''\n }\n },\n)\n\nwatch(query, () => {\n activeIndex.value = 0\n emitSearch()\n})\n\nonMounted(() => document.addEventListener('keydown', onGlobalKeydown))\nonBeforeUnmount(() => {\n document.removeEventListener('keydown', onGlobalKeydown)\n if (debounceTimer) clearTimeout(debounceTimer)\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-spot\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-black/50 pt-[12vh]\"\n @click.self=\"close\"\n >\n <div class=\"spot-box flex w-full max-w-xl flex-col overflow-hidden rounded-2xl bg-surface-container-high shadow-elevation-3\">\n <!-- Search bar -->\n <div class=\"flex items-center gap-3 px-5 py-1\">\n <MIcon name=\"search\" :size=\"24\" class=\"shrink-0 text-primary\" />\n <input\n ref=\"inputRef\"\n v-model=\"query\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"h-14 flex-1 bg-transparent text-title-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n @keydown=\"onKeydown\"\n />\n <MSpinner v-if=\"loading\" :size=\"20\" class=\"shrink-0 text-primary\" />\n <button\n v-else-if=\"hasQuery\"\n type=\"button\"\n class=\"flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-full text-on-surface-variant hover:bg-on-surface/8\"\n @click=\"query = ''\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <!-- Results -->\n <div v-if=\"hasQuery\" class=\"max-h-96 overflow-y-auto border-t border-outline-variant\">\n <template v-if=\"results.length\">\n <template v-for=\"[category, items] in grouped\" :key=\"category\">\n <p v-if=\"category\" class=\"px-5 pt-4 pb-1 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ category }}\n </p>\n <button\n v-for=\"item in items\"\n :key=\"item.id\"\n type=\"button\"\n :data-spot-active=\"results.indexOf(item) === activeIndex || undefined\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-5 py-3 text-left transition-colors\"\n :class=\"results.indexOf(item) === activeIndex ? 'bg-primary/12' : 'hover:bg-on-surface/4'\"\n @click=\"selectResult(item)\"\n @pointerenter=\"activeIndex = results.indexOf(item)\"\n >\n <div\n v-if=\"item.icon\"\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary-container\"\n >\n <MIcon :name=\"item.icon\" :size=\"20\" class=\"text-on-primary-container\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-body-medium text-on-surface\">{{ item.title }}</p>\n <p v-if=\"item.description\" class=\"truncate text-body-small text-on-surface-variant\">\n {{ item.description }}\n </p>\n </div>\n <MIcon name=\"arrow_forward\" :size=\"16\" class=\"shrink-0 text-on-surface-variant/40\" />\n </button>\n </template>\n </template>\n <div v-else-if=\"!loading\" class=\"flex flex-col items-center gap-2 py-10\">\n <MIcon name=\"search_off\" :size=\"40\" class=\"text-on-surface-variant/40\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ noResultsText }}</p>\n </div>\n </div>\n\n <!-- Hints -->\n <div class=\"flex items-center gap-4 border-t border-outline-variant px-5 py-2\">\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↑↓</kbd> navegar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↵</kbd> abrir\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">esc</kbd> cerrar\n </span>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-spot-enter-active,\n.m3-spot-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-spot-enter-from,\n.m3-spot-leave-to {\n opacity: 0;\n}\n.m3-spot-enter-active .spot-box,\n.m3-spot-leave-active .spot-box {\n transition: transform 0.15s ease, opacity 0.15s ease;\n}\n.m3-spot-enter-from .spot-box {\n transform: scale(0.96) translateY(-8px);\n opacity: 0;\n}\n.m3-spot-leave-to .spot-box {\n transform: scale(0.98);\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MSpinner from './MSpinner.vue'\n\nexport interface SpotlightResult {\n id: string | number\n title: string\n description?: string\n icon?: string\n category?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n results?: SpotlightResult[]\n placeholder?: string\n loading?: boolean\n noResultsText?: string\n hotkey?: string\n debounce?: number\n }>(),\n {\n results: () => [],\n placeholder: 'Buscar...',\n loading: false,\n noResultsText: 'No se encontraron resultados',\n hotkey: '/',\n debounce: 0,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n search: [string]\n select: [SpotlightResult]\n}>()\n\nconst query = ref('')\nconst activeIndex = ref(0)\nconst inputRef = ref<HTMLInputElement | null>(null)\nlet debounceTimer: ReturnType<typeof setTimeout> | null = null\n\nconst hasQuery = computed(() => query.value.trim().length > 0)\n\nconst grouped = computed(() => {\n const map = new Map<string, SpotlightResult[]>()\n for (const r of props.results) {\n const cat = r.category ?? ''\n if (!map.has(cat)) map.set(cat, [])\n map.get(cat)!.push(r)\n }\n return map\n})\n\nfunction close() {\n query.value = ''\n activeIndex.value = 0\n emit('update:modelValue', false)\n}\n\nfunction selectResult(result: SpotlightResult) {\n emit('select', result)\n close()\n}\n\nfunction emitSearch() {\n if (debounceTimer) clearTimeout(debounceTimer)\n if (props.debounce > 0) {\n debounceTimer = setTimeout(() => emit('search', query.value), props.debounce)\n } else {\n emit('search', query.value)\n }\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n const len = props.results.length\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n activeIndex.value = len ? (activeIndex.value + 1) % len : 0\n scrollToActive()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n activeIndex.value = len ? (activeIndex.value - 1 + len) % len : 0\n scrollToActive()\n } else if (e.key === 'Enter' && len) {\n e.preventDefault()\n selectResult(props.results[activeIndex.value]!)\n } else if (e.key === 'Escape') {\n close()\n }\n}\n\nfunction scrollToActive() {\n nextTick(() => {\n const el = document.querySelector('[data-spot-active=\"true\"]')\n el?.scrollIntoView({ block: 'nearest' })\n })\n}\n\nfunction onGlobalKeydown(e: KeyboardEvent) {\n const tag = (e.target as HTMLElement).tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable) return\n if (e.key === props.hotkey && !e.metaKey && !e.ctrlKey && !e.altKey) {\n e.preventDefault()\n emit('update:modelValue', true)\n }\n}\n\nwatch(\n () => props.modelValue,\n (open) => {\n if (open) {\n document.body.style.overflow = 'hidden'\n nextTick(() => inputRef.value?.focus())\n } else {\n document.body.style.overflow = ''\n }\n },\n)\n\nwatch(query, () => {\n activeIndex.value = 0\n emitSearch()\n})\n\nonMounted(() => document.addEventListener('keydown', onGlobalKeydown))\nonBeforeUnmount(() => {\n document.removeEventListener('keydown', onGlobalKeydown)\n if (debounceTimer) clearTimeout(debounceTimer)\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"m3-spot\">\n <div\n v-if=\"modelValue\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-black/50 pt-[12vh]\"\n @click.self=\"close\"\n >\n <div class=\"spot-box flex w-full max-w-xl flex-col overflow-hidden rounded-2xl bg-surface-container-high shadow-elevation-3\">\n <!-- Search bar -->\n <div class=\"flex items-center gap-3 px-5 py-1\">\n <MIcon name=\"search\" :size=\"24\" class=\"shrink-0 text-primary\" />\n <input\n ref=\"inputRef\"\n v-model=\"query\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"h-14 flex-1 bg-transparent text-title-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n @keydown=\"onKeydown\"\n />\n <MSpinner v-if=\"loading\" :size=\"20\" class=\"shrink-0 text-primary\" />\n <button\n v-else-if=\"hasQuery\"\n type=\"button\"\n class=\"flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-full text-on-surface-variant hover:bg-on-surface/8\"\n @click=\"query = ''\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <!-- Results -->\n <div v-if=\"hasQuery\" class=\"max-h-96 overflow-y-auto border-t border-outline-variant\">\n <template v-if=\"results.length\">\n <template v-for=\"[category, items] in grouped\" :key=\"category\">\n <p v-if=\"category\" class=\"px-5 pt-4 pb-1 text-label-small font-medium tracking-wide text-on-surface-variant uppercase\">\n {{ category }}\n </p>\n <button\n v-for=\"item in items\"\n :key=\"item.id\"\n type=\"button\"\n :data-spot-active=\"results.indexOf(item) === activeIndex || undefined\"\n class=\"flex w-full cursor-pointer items-center gap-3 px-5 py-3 text-left transition-colors\"\n :class=\"results.indexOf(item) === activeIndex ? 'bg-primary/12' : 'hover:bg-on-surface/4'\"\n @click=\"selectResult(item)\"\n @pointerenter=\"activeIndex = results.indexOf(item)\"\n >\n <div\n v-if=\"item.icon\"\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary-container\"\n >\n <MIcon :name=\"item.icon\" :size=\"20\" class=\"text-on-primary-container\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-body-medium text-on-surface\">{{ item.title }}</p>\n <p v-if=\"item.description\" class=\"truncate text-body-small text-on-surface-variant\">\n {{ item.description }}\n </p>\n </div>\n <MIcon name=\"arrow_forward\" :size=\"16\" class=\"shrink-0 text-on-surface-variant/40\" />\n </button>\n </template>\n </template>\n <div v-else-if=\"!loading\" class=\"flex flex-col items-center gap-2 py-10\">\n <MIcon name=\"search_off\" :size=\"40\" class=\"text-on-surface-variant/40\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ noResultsText }}</p>\n </div>\n </div>\n\n <!-- Hints -->\n <div class=\"flex items-center gap-4 border-t border-outline-variant px-5 py-2\">\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↑↓</kbd> navegar\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">↵</kbd> abrir\n </span>\n <span class=\"flex items-center gap-1 text-label-small text-on-surface-variant\">\n <kbd class=\"rounded bg-surface-container px-1 py-0.5\">esc</kbd> cerrar\n </span>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.m3-spot-enter-active,\n.m3-spot-leave-active {\n transition: opacity 0.15s ease;\n}\n.m3-spot-enter-from,\n.m3-spot-leave-to {\n opacity: 0;\n}\n.m3-spot-enter-active .spot-box,\n.m3-spot-leave-active .spot-box {\n transition: transform 0.15s ease, opacity 0.15s ease;\n}\n.m3-spot-enter-from .spot-box {\n transform: scale(0.96) translateY(-8px);\n opacity: 0;\n}\n.m3-spot-leave-to .spot-box {\n transform: scale(0.98);\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n direction?: 'column' | 'row'\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline'\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'\n wrap?: boolean\n divider?: boolean\n inline?: boolean\n }>(),\n { direction: 'column', gap: 'md', align: 'stretch', justify: 'start', wrap: false, divider: false, inline: false },\n)\n\nconst gapClasses: Record<string, string> = {\n none: 'gap-0',\n xs: 'gap-1',\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n xl: 'gap-8',\n}\n\nconst alignClasses: Record<string, string> = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n baseline: 'items-baseline',\n}\n\nconst justifyClasses: Record<string, string> = {\n start: 'justify-start',\n center: 'justify-center',\n end: 'justify-end',\n between: 'justify-between',\n around: 'justify-around',\n evenly: 'justify-evenly',\n}\n\nconst classes = computed(() => [\n props.inline ? 'inline-flex' : 'flex',\n props.direction === 'row' ? 'flex-row' : 'flex-col',\n !props.divider && gapClasses[props.gap],\n alignClasses[props.align],\n justifyClasses[props.justify],\n props.wrap && 'flex-wrap',\n])\n\nconst dividerClass = computed(() =>\n props.direction === 'row' ? 'w-px self-stretch bg-outline-variant' : 'h-px w-full bg-outline-variant',\n)\n</script>\n\n<template>\n <div :class=\"classes\">\n <template v-if=\"divider\">\n <template v-for=\"(_, i) in ($slots.default?.() ?? [])\" :key=\"i\">\n <div v-if=\"i > 0\" :class=\"dividerClass\" role=\"separator\" />\n <component :is=\"_\" />\n </template>\n </template>\n <slot v-else />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n direction?: 'column' | 'row'\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline'\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'\n wrap?: boolean\n divider?: boolean\n inline?: boolean\n }>(),\n { direction: 'column', gap: 'md', align: 'stretch', justify: 'start', wrap: false, divider: false, inline: false },\n)\n\nconst gapClasses: Record<string, string> = {\n none: 'gap-0',\n xs: 'gap-1',\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n xl: 'gap-8',\n}\n\nconst alignClasses: Record<string, string> = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n baseline: 'items-baseline',\n}\n\nconst justifyClasses: Record<string, string> = {\n start: 'justify-start',\n center: 'justify-center',\n end: 'justify-end',\n between: 'justify-between',\n around: 'justify-around',\n evenly: 'justify-evenly',\n}\n\nconst classes = computed(() => [\n props.inline ? 'inline-flex' : 'flex',\n props.direction === 'row' ? 'flex-row' : 'flex-col',\n !props.divider && gapClasses[props.gap],\n alignClasses[props.align],\n justifyClasses[props.justify],\n props.wrap && 'flex-wrap',\n])\n\nconst dividerClass = computed(() =>\n props.direction === 'row' ? 'w-px self-stretch bg-outline-variant' : 'h-px w-full bg-outline-variant',\n)\n</script>\n\n<template>\n <div :class=\"classes\">\n <template v-if=\"divider\">\n <template v-for=\"(_, i) in ($slots.default?.() ?? [])\" :key=\"i\">\n <div v-if=\"i > 0\" :class=\"dividerClass\" role=\"separator\" />\n <component :is=\"_\" />\n </template>\n </template>\n <slot v-else />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n title: string\n value: string | number\n icon?: string\n trend?: number\n trendLabel?: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n loading?: boolean\n}>(), { color: 'primary' })\n\nconst iconBg: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container',\n secondary: 'bg-secondary-container text-on-secondary-container',\n tertiary: 'bg-tertiary-container text-on-tertiary-container',\n error: 'bg-error-container text-on-error-container',\n success: 'bg-success-container text-on-success-container',\n}\n\nconst trendColor = computed(() => {\n if (props.trend == null) return ''\n return props.trend > 0 ? 'text-success' : props.trend < 0 ? 'text-error' : 'text-on-surface-variant'\n})\nconst trendIcon = computed(() => {\n if (props.trend == null) return ''\n return props.trend > 0 ? 'trending_up' : props.trend < 0 ? 'trending_down' : 'trending_flat'\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-3 rounded-lg border border-outline-variant bg-surface-container-lowest p-5\">\n <div class=\"flex items-start justify-between\">\n <div class=\"flex flex-col gap-1\">\n <span class=\"text-label-large text-on-surface-variant\">{{ title }}</span>\n <template v-if=\"loading\">\n <div class=\"h-8 w-24 animate-pulse rounded-md bg-on-surface/10\" />\n </template>\n <span v-else class=\"text-headline-medium font-medium text-on-surface\">{{ value }}</span>\n </div>\n <div v-if=\"icon\" class=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl\" :class=\"iconBg[color]\">\n <MIcon :name=\"icon\" :size=\"24\" />\n </div>\n </div>\n <div v-if=\"trend != null || trendLabel || $slots.footer\" class=\"flex items-center gap-2\">\n <span v-if=\"trend != null\" class=\"inline-flex items-center gap-0.5 text-label-medium font-medium\" :class=\"trendColor\">\n <MIcon :name=\"trendIcon\" :size=\"16\" />\n {{ trend > 0 ? '+' : '' }}{{ trend }}%\n </span>\n <span v-if=\"trendLabel\" class=\"text-label-medium text-on-surface-variant\">{{ trendLabel }}</span>\n <slot name=\"footer\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n title: string\n value: string | number\n icon?: string\n trend?: number\n trendLabel?: string\n color?: 'primary' | 'secondary' | 'tertiary' | 'error' | 'success'\n loading?: boolean\n}>(), { color: 'primary' })\n\nconst iconBg: Record<string, string> = {\n primary: 'bg-primary-container text-on-primary-container',\n secondary: 'bg-secondary-container text-on-secondary-container',\n tertiary: 'bg-tertiary-container text-on-tertiary-container',\n error: 'bg-error-container text-on-error-container',\n success: 'bg-success-container text-on-success-container',\n}\n\nconst trendColor = computed(() => {\n if (props.trend == null) return ''\n return props.trend > 0 ? 'text-success' : props.trend < 0 ? 'text-error' : 'text-on-surface-variant'\n})\nconst trendIcon = computed(() => {\n if (props.trend == null) return ''\n return props.trend > 0 ? 'trending_up' : props.trend < 0 ? 'trending_down' : 'trending_flat'\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-3 rounded-lg border border-outline-variant bg-surface-container-lowest p-5\">\n <div class=\"flex items-start justify-between\">\n <div class=\"flex flex-col gap-1\">\n <span class=\"text-label-large text-on-surface-variant\">{{ title }}</span>\n <template v-if=\"loading\">\n <div class=\"h-8 w-24 animate-pulse rounded-md bg-on-surface/10\" />\n </template>\n <span v-else class=\"text-headline-medium font-medium text-on-surface\">{{ value }}</span>\n </div>\n <div v-if=\"icon\" class=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl\" :class=\"iconBg[color]\">\n <MIcon :name=\"icon\" :size=\"24\" />\n </div>\n </div>\n <div v-if=\"trend != null || trendLabel || $slots.footer\" class=\"flex items-center gap-2\">\n <span v-if=\"trend != null\" class=\"inline-flex items-center gap-0.5 text-label-medium font-medium\" :class=\"trendColor\">\n <MIcon :name=\"trendIcon\" :size=\"16\" />\n {{ trend > 0 ? '+' : '' }}{{ trend }}%\n </span>\n <span v-if=\"trendLabel\" class=\"text-label-medium text-on-surface-variant\">{{ trendLabel }}</span>\n <slot name=\"footer\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface StepItem {\n label: string\n description?: string\n icon?: string\n optional?: boolean\n error?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n steps: StepItem[]\n modelValue: number\n direction?: 'horizontal' | 'vertical'\n linear?: boolean\n}>(), { direction: 'horizontal', linear: true })\n\nconst emit = defineEmits<{ 'update:modelValue': [number] }>()\n\nfunction stepState(index: number) {\n if (props.steps[index]?.error) return 'error'\n if (index < props.modelValue) return 'completed'\n if (index === props.modelValue) return 'active'\n return 'inactive'\n}\n\nconst canClick = computed(() => !props.linear)\n\nfunction select(index: number) {\n if (canClick.value) emit('update:modelValue', index)\n}\n</script>\n\n<template>\n <!-- Horizontal -->\n <div\n v-if=\"direction === 'horizontal'\"\n class=\"flex w-full items-start\"\n >\n <template v-for=\"(step, i) in steps\" :key=\"i\">\n <!-- Step -->\n <div\n class=\"flex flex-col items-center gap-2\"\n :class=\"canClick && stepState(i) !== 'active' ? 'cursor-pointer' : ''\"\n @click=\"select(i)\"\n >\n <!-- Circle -->\n <div\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-label-large font-medium transition-colors duration-200\"\n :class=\"{\n 'bg-primary text-on-primary': stepState(i) === 'active' || stepState(i) === 'completed',\n 'bg-error text-on-error': stepState(i) === 'error',\n 'bg-surface-container-highest text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n <MIcon v-if=\"stepState(i) === 'completed'\" name=\"check\" :size=\"20\" />\n <MIcon v-else-if=\"stepState(i) === 'error'\" name=\"priority_high\" :size=\"20\" />\n <MIcon v-else-if=\"step.icon\" :name=\"step.icon\" :size=\"20\" />\n <span v-else>{{ i + 1 }}</span>\n </div>\n\n <!-- Label -->\n <div class=\"flex flex-col items-center text-center\">\n <span\n class=\"text-label-large\"\n :class=\"{\n 'font-medium text-on-surface': stepState(i) === 'active' || stepState(i) === 'completed',\n 'text-error': stepState(i) === 'error',\n 'text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n {{ step.label }}\n </span>\n <span v-if=\"step.description\" class=\"text-body-small text-on-surface-variant\">\n {{ step.description }}\n </span>\n <span v-if=\"step.optional\" class=\"text-body-small text-on-surface-variant\">\n Opcional\n </span>\n </div>\n </div>\n\n <!-- Connector -->\n <div\n v-if=\"i < steps.length - 1\"\n class=\"mt-[18px] flex flex-1 items-center px-2\"\n >\n <div\n class=\"h-[1px] w-full transition-colors duration-300\"\n :class=\"i < modelValue ? 'bg-primary' : 'bg-outline-variant'\"\n />\n </div>\n </template>\n </div>\n\n <!-- Vertical -->\n <div v-else class=\"flex flex-col\">\n <template v-for=\"(step, i) in steps\" :key=\"i\">\n <div class=\"flex gap-4\">\n <!-- Left column: circle + connector -->\n <div class=\"flex flex-col items-center\">\n <div\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-label-large font-medium transition-colors duration-200\"\n :class=\"[\n {\n 'bg-primary text-on-primary': stepState(i) === 'active' || stepState(i) === 'completed',\n 'bg-error text-on-error': stepState(i) === 'error',\n 'bg-surface-container-highest text-on-surface-variant': stepState(i) === 'inactive',\n },\n canClick && stepState(i) !== 'active' ? 'cursor-pointer' : '',\n ]\"\n @click=\"select(i)\"\n >\n <MIcon v-if=\"stepState(i) === 'completed'\" name=\"check\" :size=\"20\" />\n <MIcon v-else-if=\"stepState(i) === 'error'\" name=\"priority_high\" :size=\"20\" />\n <MIcon v-else-if=\"step.icon\" :name=\"step.icon\" :size=\"20\" />\n <span v-else>{{ i + 1 }}</span>\n </div>\n <!-- Vertical connector -->\n <div\n v-if=\"i < steps.length - 1\"\n class=\"my-1 w-[1px] flex-1 transition-colors duration-300\"\n :class=\"i < modelValue ? 'bg-primary' : 'bg-outline-variant'\"\n style=\"min-height: 24px\"\n />\n </div>\n\n <!-- Right column: label + content -->\n <div\n class=\"pb-6\"\n :class=\"canClick && stepState(i) !== 'active' ? 'cursor-pointer' : ''\"\n @click=\"select(i)\"\n >\n <span\n class=\"text-label-large\"\n :class=\"{\n 'font-medium text-on-surface': stepState(i) === 'active' || stepState(i) === 'completed',\n 'text-error': stepState(i) === 'error',\n 'text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n {{ step.label }}\n </span>\n <p v-if=\"step.description\" class=\"mt-0.5 text-body-small text-on-surface-variant\">\n {{ step.description }}\n </p>\n <p v-if=\"step.optional\" class=\"text-body-small text-on-surface-variant\">\n Opcional\n </p>\n\n <!-- Slot for step content when active -->\n <div v-if=\"stepState(i) === 'active' && $slots[`step-${i}`]\" class=\"mt-3\">\n <slot :name=\"`step-${i}`\" />\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface StepItem {\n label: string\n description?: string\n icon?: string\n optional?: boolean\n error?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n steps: StepItem[]\n modelValue: number\n direction?: 'horizontal' | 'vertical'\n linear?: boolean\n}>(), { direction: 'horizontal', linear: true })\n\nconst emit = defineEmits<{ 'update:modelValue': [number] }>()\n\nfunction stepState(index: number) {\n if (props.steps[index]?.error) return 'error'\n if (index < props.modelValue) return 'completed'\n if (index === props.modelValue) return 'active'\n return 'inactive'\n}\n\nconst canClick = computed(() => !props.linear)\n\nfunction select(index: number) {\n if (canClick.value) emit('update:modelValue', index)\n}\n</script>\n\n<template>\n <!-- Horizontal -->\n <div\n v-if=\"direction === 'horizontal'\"\n class=\"flex w-full items-start\"\n >\n <template v-for=\"(step, i) in steps\" :key=\"i\">\n <!-- Step -->\n <div\n class=\"flex flex-col items-center gap-2\"\n :class=\"canClick && stepState(i) !== 'active' ? 'cursor-pointer' : ''\"\n @click=\"select(i)\"\n >\n <!-- Circle -->\n <div\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-label-large font-medium transition-colors duration-200\"\n :class=\"{\n 'bg-primary text-on-primary': stepState(i) === 'active' || stepState(i) === 'completed',\n 'bg-error text-on-error': stepState(i) === 'error',\n 'bg-surface-container-highest text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n <MIcon v-if=\"stepState(i) === 'completed'\" name=\"check\" :size=\"20\" />\n <MIcon v-else-if=\"stepState(i) === 'error'\" name=\"priority_high\" :size=\"20\" />\n <MIcon v-else-if=\"step.icon\" :name=\"step.icon\" :size=\"20\" />\n <span v-else>{{ i + 1 }}</span>\n </div>\n\n <!-- Label -->\n <div class=\"flex flex-col items-center text-center\">\n <span\n class=\"text-label-large\"\n :class=\"{\n 'font-medium text-on-surface': stepState(i) === 'active' || stepState(i) === 'completed',\n 'text-error': stepState(i) === 'error',\n 'text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n {{ step.label }}\n </span>\n <span v-if=\"step.description\" class=\"text-body-small text-on-surface-variant\">\n {{ step.description }}\n </span>\n <span v-if=\"step.optional\" class=\"text-body-small text-on-surface-variant\">\n Opcional\n </span>\n </div>\n </div>\n\n <!-- Connector -->\n <div\n v-if=\"i < steps.length - 1\"\n class=\"mt-[18px] flex flex-1 items-center px-2\"\n >\n <div\n class=\"h-[1px] w-full transition-colors duration-300\"\n :class=\"i < modelValue ? 'bg-primary' : 'bg-outline-variant'\"\n />\n </div>\n </template>\n </div>\n\n <!-- Vertical -->\n <div v-else class=\"flex flex-col\">\n <template v-for=\"(step, i) in steps\" :key=\"i\">\n <div class=\"flex gap-4\">\n <!-- Left column: circle + connector -->\n <div class=\"flex flex-col items-center\">\n <div\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-label-large font-medium transition-colors duration-200\"\n :class=\"[\n {\n 'bg-primary text-on-primary': stepState(i) === 'active' || stepState(i) === 'completed',\n 'bg-error text-on-error': stepState(i) === 'error',\n 'bg-surface-container-highest text-on-surface-variant': stepState(i) === 'inactive',\n },\n canClick && stepState(i) !== 'active' ? 'cursor-pointer' : '',\n ]\"\n @click=\"select(i)\"\n >\n <MIcon v-if=\"stepState(i) === 'completed'\" name=\"check\" :size=\"20\" />\n <MIcon v-else-if=\"stepState(i) === 'error'\" name=\"priority_high\" :size=\"20\" />\n <MIcon v-else-if=\"step.icon\" :name=\"step.icon\" :size=\"20\" />\n <span v-else>{{ i + 1 }}</span>\n </div>\n <!-- Vertical connector -->\n <div\n v-if=\"i < steps.length - 1\"\n class=\"my-1 w-[1px] flex-1 transition-colors duration-300\"\n :class=\"i < modelValue ? 'bg-primary' : 'bg-outline-variant'\"\n style=\"min-height: 24px\"\n />\n </div>\n\n <!-- Right column: label + content -->\n <div\n class=\"pb-6\"\n :class=\"canClick && stepState(i) !== 'active' ? 'cursor-pointer' : ''\"\n @click=\"select(i)\"\n >\n <span\n class=\"text-label-large\"\n :class=\"{\n 'font-medium text-on-surface': stepState(i) === 'active' || stepState(i) === 'completed',\n 'text-error': stepState(i) === 'error',\n 'text-on-surface-variant': stepState(i) === 'inactive',\n }\"\n >\n {{ step.label }}\n </span>\n <p v-if=\"step.description\" class=\"mt-0.5 text-body-small text-on-surface-variant\">\n {{ step.description }}\n </p>\n <p v-if=\"step.optional\" class=\"text-body-small text-on-surface-variant\">\n Opcional\n </p>\n\n <!-- Slot for step content when active -->\n <div v-if=\"stepState(i) === 'active' && $slots[`step-${i}`]\" class=\"mt-3\">\n <slot :name=\"`step-${i}`\" />\n </div>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{ modelValue: boolean; disabled?: boolean; label?: string }>(),\n { disabled: false },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\n// All thumb transforms live here so each direction can use its own easing curve.\n// translateY(-50%) vertically centres the 24px thumb in the 32px track.\n// ON → spring cubic-bezier: overshoots ~2px then settles → satisfying \"click\"\n// OFF → M3 standard decelerate: clean snap back, no undershoot\nconst thumbStyle = computed(() => ({\n transform: props.modelValue\n ? 'translateY(-50%) translateX(18px) scale(1)'\n : 'translateY(-50%) translateX(0px) scale(0.667)',\n transition: props.modelValue\n ? 'transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1), background-color 280ms ease'\n : 'transform 240ms cubic-bezier(0.2, 0, 0, 1), background-color 240ms ease',\n}))\n</script>\n\n<template>\n <label\n class=\"inline-flex items-center gap-3 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span\n class=\"relative inline-flex h-8 w-[52px] shrink-0 items-center rounded-full border-2 transition-colors duration-200\"\n :class=\"modelValue ? 'border-primary bg-primary' : 'border-outline bg-surface-container-highest'\"\n >\n <input\n type=\"checkbox\"\n class=\"sr-only\"\n :checked=\"modelValue\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', !modelValue)\"\n />\n\n <!-- Thumb: position + size animated via inline style (allows per-direction easing) -->\n <span\n class=\"absolute left-1 top-1/2 flex h-6 w-6 items-center justify-center rounded-full will-change-transform\"\n :class=\"modelValue ? 'bg-on-primary shadow-sm' : 'bg-outline'\"\n :style=\"thumbStyle\"\n >\n <Transition\n enter-active-class=\"transition-opacity duration-150 delay-[120ms]\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <MIcon v-if=\"modelValue\" name=\"check\" :size=\"14\" class=\"text-primary\" />\n </Transition>\n </span>\n </span>\n <span v-if=\"label\" class=\"text-body-large text-on-surface\">{{ label }}</span>\n </label>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(\n defineProps<{ modelValue: boolean; disabled?: boolean; label?: string }>(),\n { disabled: false },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [boolean] }>()\n\n// All thumb transforms live here so each direction can use its own easing curve.\n// translateY(-50%) vertically centres the 24px thumb in the 32px track.\n// ON → spring cubic-bezier: overshoots ~2px then settles → satisfying \"click\"\n// OFF → M3 standard decelerate: clean snap back, no undershoot\nconst thumbStyle = computed(() => ({\n transform: props.modelValue\n ? 'translateY(-50%) translateX(18px) scale(1)'\n : 'translateY(-50%) translateX(0px) scale(0.667)',\n transition: props.modelValue\n ? 'transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1), background-color 280ms ease'\n : 'transform 240ms cubic-bezier(0.2, 0, 0, 1), background-color 240ms ease',\n}))\n</script>\n\n<template>\n <label\n class=\"inline-flex items-center gap-3 select-none\"\n :class=\"disabled ? 'cursor-not-allowed opacity-[0.38]' : 'cursor-pointer'\"\n >\n <span\n class=\"relative inline-flex h-8 w-[52px] shrink-0 items-center rounded-full border-2 transition-colors duration-200\"\n :class=\"modelValue ? 'border-primary bg-primary' : 'border-outline bg-surface-container-highest'\"\n >\n <input\n type=\"checkbox\"\n class=\"sr-only\"\n :checked=\"modelValue\"\n :disabled=\"disabled\"\n @change=\"emit('update:modelValue', !modelValue)\"\n />\n\n <!-- Thumb: position + size animated via inline style (allows per-direction easing) -->\n <span\n class=\"absolute left-1 top-1/2 flex h-6 w-6 items-center justify-center rounded-full will-change-transform\"\n :class=\"modelValue ? 'bg-on-primary shadow-sm' : 'bg-outline'\"\n :style=\"thumbStyle\"\n >\n <Transition\n enter-active-class=\"transition-opacity duration-150 delay-[120ms]\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <MIcon v-if=\"modelValue\" name=\"check\" :size=\"14\" class=\"text-primary\" />\n </Transition>\n </span>\n </span>\n <span v-if=\"label\" class=\"text-body-large text-on-surface\">{{ label }}</span>\n </label>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport MPagination from './MPagination.vue'\n\nexport interface TableColumn {\n key: string\n label: string\n sortable?: boolean\n width?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport interface TableFetchParams {\n page: number\n perPage: number\n search: string\n sortKey: string\n sortDir: 'asc' | 'desc' | ''\n}\n\n// Static widths so skeleton markup is stable across re-renders\nconst SKEL = [65, 80, 50, 75, 90, 55, 70, 85, 60, 78, 88, 52, 70, 83, 58]\n\nconst props = withDefaults(\n defineProps<{\n columns: TableColumn[]\n rows: Record<string, any>[]\n loading?: boolean\n emptyText?: string\n rowKey?: string\n selectable?: boolean\n modelValue?: Record<string, any>[]\n perPage?: number\n searchable?: boolean\n /** Emit `fetch` instead of filtering locally. Requires :total. */\n serverSide?: boolean\n total?: number\n page?: number\n }>(),\n {\n loading: false,\n emptyText: 'Sin resultados',\n rowKey: 'id',\n selectable: false,\n modelValue: () => [],\n perPage: 10,\n searchable: true,\n serverSide: false,\n total: 0,\n page: 1,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [Record<string, any>[]]\n 'update:page': [number]\n fetch: [TableFetchParams]\n}>()\n\n// ── Search ─────────────────────────────────────────────────────────────────\nconst search = ref('')\n\n// ── Sort ───────────────────────────────────────────────────────────────────\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) {\n sortKey.value = key\n sortDir.value = 'asc'\n } else if (sortDir.value === 'asc') {\n sortDir.value = 'desc'\n } else {\n sortKey.value = ''\n sortDir.value = ''\n }\n}\n\n// ── Pagination ─────────────────────────────────────────────────────────────\nconst internalPage = ref(1)\n\nconst currentPage = computed({\n get: () => (props.serverSide ? (props.page ?? 1) : internalPage.value),\n set: (val: number) => {\n if (props.serverSide) emit('update:page', val)\n else internalPage.value = val\n },\n})\n\n// ── Client-side data processing ────────────────────────────────────────────\nconst processedRows = computed(() => {\n if (props.serverSide) return props.rows\n\n let result = props.rows\n\n if (search.value.trim()) {\n const q = search.value.toLowerCase()\n result = result.filter((row) =>\n props.columns.some((col) => {\n const val = row[col.key]\n return val != null && String(val).toLowerCase().includes(q)\n }),\n )\n }\n\n if (sortKey.value && sortDir.value) {\n const key = sortKey.value\n const dir = sortDir.value\n result = [...result].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, {\n numeric: true,\n sensitivity: 'base',\n })\n return dir === 'asc' ? cmp : -cmp\n })\n }\n\n return result\n})\n\nconst totalCount = computed(() =>\n props.serverSide ? (props.total ?? 0) : processedRows.value.length,\n)\n\nconst visibleRows = computed(() => {\n if (props.serverSide) return props.rows\n const start = (currentPage.value - 1) * props.perPage\n return processedRows.value.slice(start, start + props.perPage)\n})\n\nwatch([search, sortKey, sortDir], () => {\n if (!props.serverSide) internalPage.value = 1\n})\n\n// ── Server-side fetch ──────────────────────────────────────────────────────\nconst mounted = ref(false)\n\nfunction emitFetch() {\n emit('fetch', {\n page: currentPage.value,\n perPage: props.perPage,\n search: search.value,\n sortKey: sortKey.value,\n sortDir: sortDir.value,\n })\n}\n\nonMounted(() => {\n mounted.value = true\n if (props.serverSide) emitFetch()\n})\n\nwatch([search, sortKey, sortDir], () => {\n if (!props.serverSide || !mounted.value) return\n internalPage.value = 1\n emitFetch()\n})\n\nwatch(currentPage, () => {\n if (!props.serverSide || !mounted.value) return\n emitFetch()\n})\n\n// ── Row selection ──────────────────────────────────────────────────────────\nconst selected = computed({\n get: () => props.modelValue ?? [],\n set: (val) => emit('update:modelValue', val),\n})\n\nfunction rowId(row: Record<string, any>) {\n return row[props.rowKey]\n}\nfunction isSelected(row: Record<string, any>) {\n return selected.value.some((r) => rowId(r) === rowId(row))\n}\nfunction toggleRow(row: Record<string, any>) {\n if (isSelected(row)) selected.value = selected.value.filter((r) => rowId(r) !== rowId(row))\n else selected.value = [...selected.value, row]\n}\n\nconst allOnPageSelected = computed(\n () => visibleRows.value.length > 0 && visibleRows.value.every((r) => isSelected(r)),\n)\nconst someOnPageSelected = computed(\n () => visibleRows.value.some((r) => isSelected(r)) && !allOnPageSelected.value,\n)\n\nfunction toggleAll() {\n if (allOnPageSelected.value) {\n selected.value = selected.value.filter(\n (r) => !visibleRows.value.some((v) => rowId(v) === rowId(r)),\n )\n } else {\n selected.value = [...selected.value, ...visibleRows.value.filter((r) => !isSelected(r))]\n }\n}\n\n// ── Helpers ────────────────────────────────────────────────────────────────\nconst extraCols = computed(\n () => (props.selectable ? 1 : 0) + (useSlots()['row-actions'] ? 1 : 0),\n)\n\nfunction alignClass(align?: string) {\n return align === 'center' ? 'text-center' : align === 'right' ? 'text-right' : 'text-left'\n}\nfunction skelWidth(ri: number, ci: number) {\n return `${SKEL[(ri * 3 + ci) % SKEL.length]}%`\n}\n\nimport { useSlots } from 'vue'\nconst slots = useSlots()\nconst hasActions = computed(() => !!slots['row-actions'])\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n\n <!-- ── Toolbar ───────────────────────────────────────────────────────── -->\n <div\n v-if=\"searchable || $slots.toolbar\"\n class=\"flex flex-wrap items-center gap-3 border-b border-outline-variant bg-surface-container-lowest px-4 py-2.5\"\n >\n <!-- Search -->\n <div v-if=\"searchable\" class=\"flex min-w-48 flex-1 items-center gap-2 rounded-full border border-outline-variant bg-surface-container px-3 py-1.5 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30 transition-[border-color,box-shadow] duration-150\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n v-model=\"search\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\"\n />\n <button\n v-if=\"search\"\n class=\"text-on-surface-variant transition-colors hover:text-on-surface\"\n @click=\"search = ''\"\n >\n <MIcon name=\"close\" :size=\"14\" />\n </button>\n </div>\n\n <!-- Extra toolbar content (filters, buttons, etc.) -->\n <slot name=\"toolbar\" />\n\n <!-- Selection count pill -->\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 scale-90\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 scale-90\"\n >\n <span\n v-if=\"selectable && selected.length > 0\"\n class=\"rounded-full bg-primary/12 px-3 py-1 text-label-small font-medium text-primary\"\n >\n {{ selected.length }} seleccionado{{ selected.length !== 1 ? 's' : '' }}\n </span>\n </Transition>\n </div>\n\n <!-- ── Table ─────────────────────────────────────────────────────────── -->\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n\n <!-- Header -->\n <thead>\n <tr class=\"bg-surface-container-high\">\n <th v-if=\"selectable\" class=\"w-12 px-4 py-3\">\n <MCheckbox\n :model-value=\"allOnPageSelected\"\n :indeterminate=\"someOnPageSelected\"\n @update:model-value=\"toggleAll\"\n />\n </th>\n <th\n v-for=\"col in columns\"\n :key=\"col.key\"\n :style=\"col.width ? { width: col.width } : undefined\"\n :class=\"[\n 'px-4 py-3 text-label-medium font-medium text-on-surface-variant whitespace-nowrap',\n alignClass(col.align),\n col.sortable\n ? 'cursor-pointer select-none hover:text-on-surface transition-colors duration-100'\n : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon\n v-if=\"sortKey === col.key && sortDir === 'asc'\"\n name=\"arrow_upward\"\n :size=\"14\"\n class=\"text-primary\"\n />\n <MIcon\n v-else-if=\"sortKey === col.key && sortDir === 'desc'\"\n name=\"arrow_downward\"\n :size=\"14\"\n class=\"text-primary\"\n />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n </th>\n <th v-if=\"hasActions\" class=\"w-1 px-4 py-3\" />\n </tr>\n </thead>\n\n <!-- Body -->\n <tbody>\n <!-- Loading skeleton -->\n <template v-if=\"loading\">\n <tr\n v-for=\"ri in perPage\"\n :key=\"`sk-${ri}`\"\n class=\"border-t border-outline-variant\"\n >\n <td v-if=\"selectable\" class=\"px-4 py-3.5\">\n <div class=\"h-4 w-4 animate-pulse rounded bg-on-surface/10\" />\n </td>\n <td\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n class=\"px-4 py-3.5\"\n >\n <div\n class=\"h-4 animate-pulse rounded-full bg-on-surface/10\"\n :style=\"{ width: skelWidth(ri, ci) }\"\n />\n </td>\n <td v-if=\"hasActions\" class=\"px-4 py-3.5\">\n <div class=\"ml-auto h-4 w-16 animate-pulse rounded-full bg-on-surface/10\" />\n </td>\n </tr>\n </template>\n\n <!-- Empty state -->\n <template v-else-if=\"visibleRows.length === 0\">\n <tr>\n <td\n :colspan=\"columns.length + extraCols\"\n class=\"border-t border-outline-variant px-4 py-14 text-center\"\n >\n <slot name=\"empty\">\n <MIcon name=\"search_off\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ emptyText }}</p>\n </slot>\n </td>\n </tr>\n </template>\n\n <!-- Data rows -->\n <template v-else>\n <tr\n v-for=\"row in visibleRows\"\n :key=\"rowId(row)\"\n :class=\"[\n 'border-t border-outline-variant transition-colors duration-100',\n 'hover:bg-on-surface/[0.04]',\n selectable && isSelected(row) ? 'bg-primary/[0.06]' : '',\n selectable ? 'cursor-pointer' : '',\n ]\"\n @click=\"selectable ? toggleRow(row) : undefined\"\n >\n <td v-if=\"selectable\" class=\"px-4 py-3\" @click.stop=\"toggleRow(row)\">\n <MCheckbox\n :model-value=\"isSelected(row)\"\n @update:model-value=\"toggleRow(row)\"\n />\n </td>\n <td\n v-for=\"col in columns\"\n :key=\"col.key\"\n :class=\"['px-4 py-3 text-body-medium text-on-surface', alignClass(col.align)]\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\" :col=\"col\">\n {{ row[col.key] ?? '—' }}\n </slot>\n </td>\n <td v-if=\"hasActions\" class=\"px-4 py-3 text-right\" @click.stop>\n <slot name=\"row-actions\" :row=\"row\" />\n </td>\n </tr>\n </template>\n </tbody>\n\n </table>\n </div>\n\n <!-- ── Footer ────────────────────────────────────────────────────────── -->\n <div class=\"border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <MPagination\n :page=\"currentPage\"\n :per-page=\"perPage\"\n :total=\"totalCount\"\n @update:page=\"currentPage = $event\"\n />\n </div>\n\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport MPagination from './MPagination.vue'\n\nexport interface TableColumn {\n key: string\n label: string\n sortable?: boolean\n width?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport interface TableFetchParams {\n page: number\n perPage: number\n search: string\n sortKey: string\n sortDir: 'asc' | 'desc' | ''\n}\n\n// Static widths so skeleton markup is stable across re-renders\nconst SKEL = [65, 80, 50, 75, 90, 55, 70, 85, 60, 78, 88, 52, 70, 83, 58]\n\nconst props = withDefaults(\n defineProps<{\n columns: TableColumn[]\n rows: Record<string, any>[]\n loading?: boolean\n emptyText?: string\n rowKey?: string\n selectable?: boolean\n modelValue?: Record<string, any>[]\n perPage?: number\n searchable?: boolean\n /** Emit `fetch` instead of filtering locally. Requires :total. */\n serverSide?: boolean\n total?: number\n page?: number\n }>(),\n {\n loading: false,\n emptyText: 'Sin resultados',\n rowKey: 'id',\n selectable: false,\n modelValue: () => [],\n perPage: 10,\n searchable: true,\n serverSide: false,\n total: 0,\n page: 1,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [Record<string, any>[]]\n 'update:page': [number]\n fetch: [TableFetchParams]\n}>()\n\n// ── Search ─────────────────────────────────────────────────────────────────\nconst search = ref('')\n\n// ── Sort ───────────────────────────────────────────────────────────────────\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) {\n sortKey.value = key\n sortDir.value = 'asc'\n } else if (sortDir.value === 'asc') {\n sortDir.value = 'desc'\n } else {\n sortKey.value = ''\n sortDir.value = ''\n }\n}\n\n// ── Pagination ─────────────────────────────────────────────────────────────\nconst internalPage = ref(1)\n\nconst currentPage = computed({\n get: () => (props.serverSide ? (props.page ?? 1) : internalPage.value),\n set: (val: number) => {\n if (props.serverSide) emit('update:page', val)\n else internalPage.value = val\n },\n})\n\n// ── Client-side data processing ────────────────────────────────────────────\nconst processedRows = computed(() => {\n if (props.serverSide) return props.rows\n\n let result = props.rows\n\n if (search.value.trim()) {\n const q = search.value.toLowerCase()\n result = result.filter((row) =>\n props.columns.some((col) => {\n const val = row[col.key]\n return val != null && String(val).toLowerCase().includes(q)\n }),\n )\n }\n\n if (sortKey.value && sortDir.value) {\n const key = sortKey.value\n const dir = sortDir.value\n result = [...result].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, {\n numeric: true,\n sensitivity: 'base',\n })\n return dir === 'asc' ? cmp : -cmp\n })\n }\n\n return result\n})\n\nconst totalCount = computed(() =>\n props.serverSide ? (props.total ?? 0) : processedRows.value.length,\n)\n\nconst visibleRows = computed(() => {\n if (props.serverSide) return props.rows\n const start = (currentPage.value - 1) * props.perPage\n return processedRows.value.slice(start, start + props.perPage)\n})\n\nwatch([search, sortKey, sortDir], () => {\n if (!props.serverSide) internalPage.value = 1\n})\n\n// ── Server-side fetch ──────────────────────────────────────────────────────\nconst mounted = ref(false)\n\nfunction emitFetch() {\n emit('fetch', {\n page: currentPage.value,\n perPage: props.perPage,\n search: search.value,\n sortKey: sortKey.value,\n sortDir: sortDir.value,\n })\n}\n\nonMounted(() => {\n mounted.value = true\n if (props.serverSide) emitFetch()\n})\n\nwatch([search, sortKey, sortDir], () => {\n if (!props.serverSide || !mounted.value) return\n internalPage.value = 1\n emitFetch()\n})\n\nwatch(currentPage, () => {\n if (!props.serverSide || !mounted.value) return\n emitFetch()\n})\n\n// ── Row selection ──────────────────────────────────────────────────────────\nconst selected = computed({\n get: () => props.modelValue ?? [],\n set: (val) => emit('update:modelValue', val),\n})\n\nfunction rowId(row: Record<string, any>) {\n return row[props.rowKey]\n}\nfunction isSelected(row: Record<string, any>) {\n return selected.value.some((r) => rowId(r) === rowId(row))\n}\nfunction toggleRow(row: Record<string, any>) {\n if (isSelected(row)) selected.value = selected.value.filter((r) => rowId(r) !== rowId(row))\n else selected.value = [...selected.value, row]\n}\n\nconst allOnPageSelected = computed(\n () => visibleRows.value.length > 0 && visibleRows.value.every((r) => isSelected(r)),\n)\nconst someOnPageSelected = computed(\n () => visibleRows.value.some((r) => isSelected(r)) && !allOnPageSelected.value,\n)\n\nfunction toggleAll() {\n if (allOnPageSelected.value) {\n selected.value = selected.value.filter(\n (r) => !visibleRows.value.some((v) => rowId(v) === rowId(r)),\n )\n } else {\n selected.value = [...selected.value, ...visibleRows.value.filter((r) => !isSelected(r))]\n }\n}\n\n// ── Helpers ────────────────────────────────────────────────────────────────\nconst extraCols = computed(\n () => (props.selectable ? 1 : 0) + (useSlots()['row-actions'] ? 1 : 0),\n)\n\nfunction alignClass(align?: string) {\n return align === 'center' ? 'text-center' : align === 'right' ? 'text-right' : 'text-left'\n}\nfunction skelWidth(ri: number, ci: number) {\n return `${SKEL[(ri * 3 + ci) % SKEL.length]}%`\n}\n\nimport { useSlots } from 'vue'\nconst slots = useSlots()\nconst hasActions = computed(() => !!slots['row-actions'])\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n\n <!-- ── Toolbar ───────────────────────────────────────────────────────── -->\n <div\n v-if=\"searchable || $slots.toolbar\"\n class=\"flex flex-wrap items-center gap-3 border-b border-outline-variant bg-surface-container-lowest px-4 py-2.5\"\n >\n <!-- Search -->\n <div v-if=\"searchable\" class=\"flex min-w-48 flex-1 items-center gap-2 rounded-full border border-outline-variant bg-surface-container px-3 py-1.5 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30 transition-[border-color,box-shadow] duration-150\">\n <MIcon name=\"search\" :size=\"16\" class=\"shrink-0 text-on-surface-variant\" />\n <input\n v-model=\"search\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant\"\n />\n <button\n v-if=\"search\"\n class=\"text-on-surface-variant transition-colors hover:text-on-surface\"\n @click=\"search = ''\"\n >\n <MIcon name=\"close\" :size=\"14\" />\n </button>\n </div>\n\n <!-- Extra toolbar content (filters, buttons, etc.) -->\n <slot name=\"toolbar\" />\n\n <!-- Selection count pill -->\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 scale-90\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 scale-90\"\n >\n <span\n v-if=\"selectable && selected.length > 0\"\n class=\"rounded-full bg-primary/12 px-3 py-1 text-label-small font-medium text-primary\"\n >\n {{ selected.length }} seleccionado{{ selected.length !== 1 ? 's' : '' }}\n </span>\n </Transition>\n </div>\n\n <!-- ── Table ─────────────────────────────────────────────────────────── -->\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n\n <!-- Header -->\n <thead>\n <tr class=\"bg-surface-container-high\">\n <th v-if=\"selectable\" class=\"w-12 px-4 py-3\">\n <MCheckbox\n :model-value=\"allOnPageSelected\"\n :indeterminate=\"someOnPageSelected\"\n @update:model-value=\"toggleAll\"\n />\n </th>\n <th\n v-for=\"col in columns\"\n :key=\"col.key\"\n :style=\"col.width ? { width: col.width } : undefined\"\n :class=\"[\n 'px-4 py-3 text-label-medium font-medium text-on-surface-variant whitespace-nowrap',\n alignClass(col.align),\n col.sortable\n ? 'cursor-pointer select-none hover:text-on-surface transition-colors duration-100'\n : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon\n v-if=\"sortKey === col.key && sortDir === 'asc'\"\n name=\"arrow_upward\"\n :size=\"14\"\n class=\"text-primary\"\n />\n <MIcon\n v-else-if=\"sortKey === col.key && sortDir === 'desc'\"\n name=\"arrow_downward\"\n :size=\"14\"\n class=\"text-primary\"\n />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n </th>\n <th v-if=\"hasActions\" class=\"w-1 px-4 py-3\" />\n </tr>\n </thead>\n\n <!-- Body -->\n <tbody>\n <!-- Loading skeleton -->\n <template v-if=\"loading\">\n <tr\n v-for=\"ri in perPage\"\n :key=\"`sk-${ri}`\"\n class=\"border-t border-outline-variant\"\n >\n <td v-if=\"selectable\" class=\"px-4 py-3.5\">\n <div class=\"h-4 w-4 animate-pulse rounded bg-on-surface/10\" />\n </td>\n <td\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n class=\"px-4 py-3.5\"\n >\n <div\n class=\"h-4 animate-pulse rounded-full bg-on-surface/10\"\n :style=\"{ width: skelWidth(ri, ci) }\"\n />\n </td>\n <td v-if=\"hasActions\" class=\"px-4 py-3.5\">\n <div class=\"ml-auto h-4 w-16 animate-pulse rounded-full bg-on-surface/10\" />\n </td>\n </tr>\n </template>\n\n <!-- Empty state -->\n <template v-else-if=\"visibleRows.length === 0\">\n <tr>\n <td\n :colspan=\"columns.length + extraCols\"\n class=\"border-t border-outline-variant px-4 py-14 text-center\"\n >\n <slot name=\"empty\">\n <MIcon name=\"search_off\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">{{ emptyText }}</p>\n </slot>\n </td>\n </tr>\n </template>\n\n <!-- Data rows -->\n <template v-else>\n <tr\n v-for=\"row in visibleRows\"\n :key=\"rowId(row)\"\n :class=\"[\n 'border-t border-outline-variant transition-colors duration-100',\n 'hover:bg-on-surface/[0.04]',\n selectable && isSelected(row) ? 'bg-primary/[0.06]' : '',\n selectable ? 'cursor-pointer' : '',\n ]\"\n @click=\"selectable ? toggleRow(row) : undefined\"\n >\n <td v-if=\"selectable\" class=\"px-4 py-3\" @click.stop=\"toggleRow(row)\">\n <MCheckbox\n :model-value=\"isSelected(row)\"\n @update:model-value=\"toggleRow(row)\"\n />\n </td>\n <td\n v-for=\"col in columns\"\n :key=\"col.key\"\n :class=\"['px-4 py-3 text-body-medium text-on-surface', alignClass(col.align)]\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\" :col=\"col\">\n {{ row[col.key] ?? '—' }}\n </slot>\n </td>\n <td v-if=\"hasActions\" class=\"px-4 py-3 text-right\" @click.stop>\n <slot name=\"row-actions\" :row=\"row\" />\n </td>\n </tr>\n </template>\n </tbody>\n\n </table>\n </div>\n\n <!-- ── Footer ────────────────────────────────────────────────────────── -->\n <div class=\"border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <MPagination\n :page=\"currentPage\"\n :per-page=\"perPage\"\n :total=\"totalCount\"\n @update:page=\"currentPage = $event\"\n />\n </div>\n\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\n\ninterface Tab {\n value: string | number\n label: string\n icon?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: string | number\n tabs: Tab[]\n variant?: 'primary' | 'secondary'\n}>(), { variant: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number] }>()\n\n// Primary tab: refs for indicator position\nconst tabEls = ref<HTMLElement[]>([])\nconst indicatorLeft = ref(0)\nconst indicatorWidth = ref(0)\n\nfunction updateIndicator() {\n nextTick(() => {\n const idx = props.tabs.findIndex((t) => t.value === props.modelValue)\n const el = tabEls.value[idx]\n if (!el) return\n indicatorLeft.value = el.offsetLeft\n indicatorWidth.value = el.offsetWidth\n })\n}\n\nonMounted(updateIndicator)\nwatch(() => props.modelValue, updateIndicator)\nwatch(() => props.tabs, updateIndicator, { deep: true })\n\nfunction select(tab: Tab) {\n if (!tab.disabled) emit('update:modelValue', tab.value)\n}\n</script>\n\n<template>\n <!-- ── Primary: underline with sliding indicator ──────────────────────── -->\n <div v-if=\"variant === 'primary'\" class=\"relative border-b border-outline-variant\">\n <div class=\"flex overflow-x-auto\" style=\"scrollbar-width: none\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.value\"\n :ref=\"(el) => { if (el) tabEls[tabs.indexOf(tab)] = el as HTMLElement }\"\n type=\"button\"\n class=\"relative flex h-12 shrink-0 flex-col items-center justify-center gap-0.5 px-6 text-label-large transition-colors duration-150 focus-visible:outline-none\"\n :class=\"[\n tab.value === modelValue\n ? 'text-primary'\n : tab.disabled\n ? 'cursor-not-allowed text-on-surface/38'\n : 'cursor-pointer text-on-surface-variant hover:text-on-surface',\n tab.icon ? 'pt-2 pb-1' : '',\n ]\"\n :disabled=\"tab.disabled\"\n @click=\"select(tab)\"\n >\n <MIcon v-if=\"tab.icon\" :name=\"tab.icon\" :size=\"20\" />\n <span>{{ tab.label }}</span>\n </button>\n </div>\n <!-- Sliding indicator -->\n <div\n class=\"absolute bottom-0 h-[3px] rounded-t-sm bg-primary transition-[left,width] duration-200 ease-[cubic-bezier(0.2,0,0,1)]\"\n :style=\"{ left: `${indicatorLeft}px`, width: `${indicatorWidth}px` }\"\n />\n </div>\n\n <!-- ── Secondary: pill style ──────────────────────────────────────────── -->\n <div v-else class=\"flex flex-wrap gap-1 rounded-full bg-surface-container p-1\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.value\"\n type=\"button\"\n class=\"flex h-8 items-center gap-2 rounded-full px-4 text-label-large transition-all duration-150 focus-visible:outline-none\"\n :class=\"\n tab.value === modelValue\n ? 'bg-secondary-container text-on-secondary-container shadow-elevation-1'\n : tab.disabled\n ? 'cursor-not-allowed text-on-surface/38'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8 hover:text-on-surface'\n \"\n :disabled=\"tab.disabled\"\n @click=\"select(tab)\"\n >\n <MIcon v-if=\"tab.icon\" :name=\"tab.icon\" :size=\"16\" />\n {{ tab.label }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\n\ninterface Tab {\n value: string | number\n label: string\n icon?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: string | number\n tabs: Tab[]\n variant?: 'primary' | 'secondary'\n}>(), { variant: 'primary' })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | number] }>()\n\n// Primary tab: refs for indicator position\nconst tabEls = ref<HTMLElement[]>([])\nconst indicatorLeft = ref(0)\nconst indicatorWidth = ref(0)\n\nfunction updateIndicator() {\n nextTick(() => {\n const idx = props.tabs.findIndex((t) => t.value === props.modelValue)\n const el = tabEls.value[idx]\n if (!el) return\n indicatorLeft.value = el.offsetLeft\n indicatorWidth.value = el.offsetWidth\n })\n}\n\nonMounted(updateIndicator)\nwatch(() => props.modelValue, updateIndicator)\nwatch(() => props.tabs, updateIndicator, { deep: true })\n\nfunction select(tab: Tab) {\n if (!tab.disabled) emit('update:modelValue', tab.value)\n}\n</script>\n\n<template>\n <!-- ── Primary: underline with sliding indicator ──────────────────────── -->\n <div v-if=\"variant === 'primary'\" class=\"relative border-b border-outline-variant\">\n <div class=\"flex overflow-x-auto\" style=\"scrollbar-width: none\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.value\"\n :ref=\"(el) => { if (el) tabEls[tabs.indexOf(tab)] = el as HTMLElement }\"\n type=\"button\"\n class=\"relative flex h-12 shrink-0 flex-col items-center justify-center gap-0.5 px-6 text-label-large transition-colors duration-150 focus-visible:outline-none\"\n :class=\"[\n tab.value === modelValue\n ? 'text-primary'\n : tab.disabled\n ? 'cursor-not-allowed text-on-surface/38'\n : 'cursor-pointer text-on-surface-variant hover:text-on-surface',\n tab.icon ? 'pt-2 pb-1' : '',\n ]\"\n :disabled=\"tab.disabled\"\n @click=\"select(tab)\"\n >\n <MIcon v-if=\"tab.icon\" :name=\"tab.icon\" :size=\"20\" />\n <span>{{ tab.label }}</span>\n </button>\n </div>\n <!-- Sliding indicator -->\n <div\n class=\"absolute bottom-0 h-[3px] rounded-t-sm bg-primary transition-[left,width] duration-200 ease-[cubic-bezier(0.2,0,0,1)]\"\n :style=\"{ left: `${indicatorLeft}px`, width: `${indicatorWidth}px` }\"\n />\n </div>\n\n <!-- ── Secondary: pill style ──────────────────────────────────────────── -->\n <div v-else class=\"flex flex-wrap gap-1 rounded-full bg-surface-container p-1\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.value\"\n type=\"button\"\n class=\"flex h-8 items-center gap-2 rounded-full px-4 text-label-large transition-all duration-150 focus-visible:outline-none\"\n :class=\"\n tab.value === modelValue\n ? 'bg-secondary-container text-on-secondary-container shadow-elevation-1'\n : tab.disabled\n ? 'cursor-not-allowed text-on-surface/38'\n : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8 hover:text-on-surface'\n \"\n :disabled=\"tab.disabled\"\n @click=\"select(tab)\"\n >\n <MIcon v-if=\"tab.icon\" :name=\"tab.icon\" :size=\"16\" />\n {{ tab.label }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, useSlots } from \"vue\";\nimport MIcon from \"./MIcon.vue\";\nimport { useFieldBg } from \"../composables/useFieldBg\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number;\n label: string;\n type?: string;\n variant?: \"filled\" | \"outlined\";\n error?: string;\n hint?: string;\n disabled?: boolean;\n required?: boolean;\n multiline?: boolean;\n rows?: number;\n autocomplete?: string;\n leadingIcon?: string;\n /**\n * Background color behind the label in outlined variant.\n * Defaults to the page surface color. Pass e.g. 'var(--color-surface-container-low)'\n * when the input is inside a card.\n */\n fieldBg?: string;\n }>(),\n {\n type: \"text\",\n variant: \"filled\",\n rows: 3,\n },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [string] }>();\n\nconst id = useId();\nconst slots = useSlots();\n\nconst fieldBgEl = ref<HTMLElement | null>(null);\nconst { resolvedFieldBg } = useFieldBg(fieldBgEl, () => props.fieldBg);\n\nconst inputClasses = computed(() => {\n const hasTrailing = !!slots.trailing;\n const pl = props.leadingIcon ? \"pl-12\" : \"pl-4\";\n const pr = hasTrailing ? \"pr-12\" : \"pr-4\";\n const size = props.multiline ? \"resize-y min-h-[56px]\" : \"h-14\";\n const base = [\n \"peer block w-full text-body-large text-on-surface outline-none placeholder:text-transparent\",\n \"transition-[border-color,border-width] duration-150\",\n \"disabled:cursor-not-allowed disabled:opacity-[0.38]\",\n size,\n pl,\n pr,\n ];\n\n if (props.variant === \"outlined\") {\n return [\n ...base,\n \"rounded-sm border bg-transparent py-4\",\n props.error\n ? \"border-error focus:border-2 focus:border-error\"\n : \"border-outline hover:border-on-surface focus:border-2 focus:border-primary\",\n ].join(\" \");\n }\n\n return [\n ...base,\n \"rounded-t-sm bg-surface-container-highest border-b pt-6 pb-2\",\n props.error\n ? \"border-error focus:border-b-2 focus:border-error\"\n : \"border-on-surface-variant hover:border-on-surface focus:border-b-2 focus:border-primary\",\n ].join(\" \");\n});\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? props.variant === \"outlined\"\n ? \"left-11\"\n : \"left-12\"\n : props.variant === \"outlined\"\n ? \"left-3\"\n : \"left-4\";\n\n const base = [\n \"pointer-events-none absolute truncate transition-all duration-200\",\n left,\n \"right-4\",\n \"top-1/2 -translate-y-1/2 text-body-large\",\n ];\n\n if (props.variant === \"outlined\") {\n // When floated: drop right-4 (right-auto) and cap max-width so the label\n // shrinks to its own text width. The bg then only covers the glyphs + px-1,\n // cutting the border just where the text sits instead of a long strip.\n return [\n ...base,\n \"peer-focus:-top-2.5 peer-focus:translate-y-0 peer-focus:text-label-small peer-focus:right-auto peer-focus:max-w-[calc(100%-1.5rem)] peer-focus:bg-[var(--field-bg)] peer-focus:px-1\",\n \"peer-[&:not(:placeholder-shown)]:-top-2.5 peer-[&:not(:placeholder-shown)]:translate-y-0 peer-[&:not(:placeholder-shown)]:right-auto peer-[&:not(:placeholder-shown)]:max-w-[calc(100%-1.5rem)]\",\n \"peer-[&:not(:placeholder-shown)]:text-label-small peer-[&:not(:placeholder-shown)]:bg-[var(--field-bg)] peer-[&:not(:placeholder-shown)]:px-1\",\n props.error\n ? \"text-error peer-focus:text-error\"\n : \"text-on-surface-variant peer-focus:text-primary\",\n ].join(\" \");\n }\n\n // Filled: label floats to top-2 (slightly higher than before)\n return [\n ...base,\n \"peer-focus:top-2 peer-focus:translate-y-0 peer-focus:text-label-small\",\n \"peer-[&:not(:placeholder-shown)]:top-2 peer-[&:not(:placeholder-shown)]:translate-y-0 peer-[&:not(:placeholder-shown)]:text-label-small\",\n props.error\n ? \"text-error peer-focus:text-error\"\n : \"text-on-surface-variant peer-focus:text-primary\",\n ].join(\" \");\n});\n\nfunction onInput(event: Event) {\n const target = event.target as HTMLInputElement | HTMLTextAreaElement;\n emit(\"update:modelValue\", target.value);\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <!--\n --field-bg: background behind the floating label in outlined mode, so it\n \"cuts through\" the border. Auto-detected from the nearest opaque ancestor\n (see resolveBg); overridable via the fieldBg prop; falls back to surface.\n -->\n <div\n ref=\"fieldBgEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <!-- Leading icon: centered in the 48px left zone (left-3.5 → center at 24px) -->\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <textarea\n v-if=\"multiline\"\n :id=\"id\"\n :value=\"String(modelValue)\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :required=\"required\"\n placeholder=\" \"\n :class=\"inputClasses\"\n @input=\"onInput\"\n />\n <input\n v-else\n :id=\"id\"\n :type=\"type\"\n :value=\"modelValue\"\n :disabled=\"disabled\"\n :required=\"required\"\n :autocomplete=\"autocomplete\"\n placeholder=\" \"\n :class=\"inputClasses\"\n @input=\"onInput\"\n />\n\n <label :for=\"id\" :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <div v-if=\"$slots.trailing\" class=\"absolute top-1/2 right-2 -translate-y-1/2\">\n <slot name=\"trailing\" />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, useSlots } from \"vue\";\nimport MIcon from \"./MIcon.vue\";\nimport { useFieldBg } from \"../composables/useFieldBg\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number;\n label: string;\n type?: string;\n variant?: \"filled\" | \"outlined\";\n error?: string;\n hint?: string;\n disabled?: boolean;\n required?: boolean;\n multiline?: boolean;\n rows?: number;\n autocomplete?: string;\n leadingIcon?: string;\n /**\n * Background color behind the label in outlined variant.\n * Defaults to the page surface color. Pass e.g. 'var(--color-surface-container-low)'\n * when the input is inside a card.\n */\n fieldBg?: string;\n }>(),\n {\n type: \"text\",\n variant: \"filled\",\n rows: 3,\n },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [string] }>();\n\nconst id = useId();\nconst slots = useSlots();\n\nconst fieldBgEl = ref<HTMLElement | null>(null);\nconst { resolvedFieldBg } = useFieldBg(fieldBgEl, () => props.fieldBg);\n\nconst inputClasses = computed(() => {\n const hasTrailing = !!slots.trailing;\n const pl = props.leadingIcon ? \"pl-12\" : \"pl-4\";\n const pr = hasTrailing ? \"pr-12\" : \"pr-4\";\n const size = props.multiline ? \"resize-y min-h-[56px]\" : \"h-14\";\n const base = [\n \"peer block w-full text-body-large text-on-surface outline-none placeholder:text-transparent\",\n \"transition-[border-color,border-width] duration-150\",\n \"disabled:cursor-not-allowed disabled:opacity-[0.38]\",\n size,\n pl,\n pr,\n ];\n\n if (props.variant === \"outlined\") {\n return [\n ...base,\n \"rounded-sm border bg-transparent py-4\",\n props.error\n ? \"border-error focus:border-2 focus:border-error\"\n : \"border-outline hover:border-on-surface focus:border-2 focus:border-primary\",\n ].join(\" \");\n }\n\n return [\n ...base,\n \"rounded-t-sm bg-surface-container-highest border-b pt-6 pb-2\",\n props.error\n ? \"border-error focus:border-b-2 focus:border-error\"\n : \"border-on-surface-variant hover:border-on-surface focus:border-b-2 focus:border-primary\",\n ].join(\" \");\n});\n\nconst labelClasses = computed(() => {\n const left = props.leadingIcon\n ? props.variant === \"outlined\"\n ? \"left-11\"\n : \"left-12\"\n : props.variant === \"outlined\"\n ? \"left-3\"\n : \"left-4\";\n\n const base = [\n \"pointer-events-none absolute truncate transition-all duration-200\",\n left,\n \"right-4\",\n \"top-1/2 -translate-y-1/2 text-body-large\",\n ];\n\n if (props.variant === \"outlined\") {\n // When floated: drop right-4 (right-auto) and cap max-width so the label\n // shrinks to its own text width. The bg then only covers the glyphs + px-1,\n // cutting the border just where the text sits instead of a long strip.\n return [\n ...base,\n \"peer-focus:-top-2.5 peer-focus:translate-y-0 peer-focus:text-label-small peer-focus:right-auto peer-focus:max-w-[calc(100%-1.5rem)] peer-focus:bg-[var(--field-bg)] peer-focus:px-1\",\n \"peer-[&:not(:placeholder-shown)]:-top-2.5 peer-[&:not(:placeholder-shown)]:translate-y-0 peer-[&:not(:placeholder-shown)]:right-auto peer-[&:not(:placeholder-shown)]:max-w-[calc(100%-1.5rem)]\",\n \"peer-[&:not(:placeholder-shown)]:text-label-small peer-[&:not(:placeholder-shown)]:bg-[var(--field-bg)] peer-[&:not(:placeholder-shown)]:px-1\",\n props.error\n ? \"text-error peer-focus:text-error\"\n : \"text-on-surface-variant peer-focus:text-primary\",\n ].join(\" \");\n }\n\n // Filled: label floats to top-2 (slightly higher than before)\n return [\n ...base,\n \"peer-focus:top-2 peer-focus:translate-y-0 peer-focus:text-label-small\",\n \"peer-[&:not(:placeholder-shown)]:top-2 peer-[&:not(:placeholder-shown)]:translate-y-0 peer-[&:not(:placeholder-shown)]:text-label-small\",\n props.error\n ? \"text-error peer-focus:text-error\"\n : \"text-on-surface-variant peer-focus:text-primary\",\n ].join(\" \");\n});\n\nfunction onInput(event: Event) {\n const target = event.target as HTMLInputElement | HTMLTextAreaElement;\n emit(\"update:modelValue\", target.value);\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <!--\n --field-bg: background behind the floating label in outlined mode, so it\n \"cuts through\" the border. Auto-detected from the nearest opaque ancestor\n (see resolveBg); overridable via the fieldBg prop; falls back to surface.\n -->\n <div\n ref=\"fieldBgEl\"\n class=\"relative\"\n :class=\"variant === 'outlined' ? 'mt-2' : ''\"\n :style=\"variant === 'outlined' ? { '--field-bg': resolvedFieldBg } : undefined\"\n >\n <!-- Leading icon: centered in the 48px left zone (left-3.5 → center at 24px) -->\n <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 top-1/2 -translate-y-1/2 text-on-surface-variant\"\n >\n <MIcon :name=\"leadingIcon\" :size=\"20\" />\n </div>\n\n <textarea\n v-if=\"multiline\"\n :id=\"id\"\n :value=\"String(modelValue)\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :required=\"required\"\n placeholder=\" \"\n :class=\"inputClasses\"\n @input=\"onInput\"\n />\n <input\n v-else\n :id=\"id\"\n :type=\"type\"\n :value=\"modelValue\"\n :disabled=\"disabled\"\n :required=\"required\"\n :autocomplete=\"autocomplete\"\n placeholder=\" \"\n :class=\"inputClasses\"\n @input=\"onInput\"\n />\n\n <label :for=\"id\" :class=\"labelClasses\">\n {{ label }}<span v-if=\"required\" class=\"text-error\"> *</span>\n </label>\n\n <div v-if=\"$slots.trailing\" class=\"absolute top-1/2 right-2 -translate-y-1/2\">\n <slot name=\"trailing\" />\n </div>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from \"./MIcon.vue\";\n\nexport interface TimelineItem {\n title: string;\n description?: string;\n date?: string;\n icon?: string;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\" | \"success\";\n dotColor?: string;\n}\n\nwithDefaults(\n defineProps<{\n items: TimelineItem[];\n dense?: boolean;\n alternating?: boolean;\n }>(),\n { dense: false, alternating: false },\n);\n\nconst dotBg: Record<string, string> = {\n primary: \"bg-primary text-on-primary\",\n secondary: \"bg-secondary text-on-secondary\",\n tertiary: \"bg-tertiary text-on-tertiary\",\n error: \"bg-error text-on-error\",\n success: \"bg-success text-on-success\",\n};\n</script>\n\n<template>\n <div :class=\"alternating ? 'relative' : 'flex flex-col'\">\n <!-- Standard layout -->\n <template v-if=\"!alternating\">\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n class=\"relative flex gap-4\"\n :class=\"dense ? 'pb-4' : 'pb-8'\"\n >\n <!-- Line + dot -->\n <div class=\"flex flex-col items-center\">\n <div\n class=\"z-[1] flex shrink-0 items-center justify-center rounded-full\"\n :class=\"[item.icon ? 'h-9 w-9' : 'h-3 w-3', dotBg[item.color ?? 'primary']]\"\n :style=\"item.dotColor ? { backgroundColor: item.dotColor } : undefined\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n </div>\n <div\n v-if=\"i < items.length - 1\"\n class=\"w-[2px] flex-1\"\n :class=\"dotBg[item.color ?? 'primary']!.split(' ')[0] + '/30'\"\n style=\"min-height: 16px\"\n />\n </div>\n\n <!-- Content -->\n <div :class=\"item.icon ? '' : 'pt-0'\" class=\"-mt-0.5 flex-1\">\n <div class=\"flex items-baseline justify-between gap-2\">\n <p class=\"text-body-large font-medium text-on-surface\">{{ item.title }}</p>\n <span v-if=\"item.date\" class=\"shrink-0 text-label-small text-on-surface-variant\">{{\n item.date\n }}</span>\n </div>\n <p v-if=\"item.description\" class=\"mt-1 text-body-medium text-on-surface-variant\">\n {{ item.description }}\n </p>\n <div v-if=\"$slots[`item-${i}`]\" class=\"mt-2\">\n <slot :name=\"`item-${i}`\" :item=\"item\" />\n </div>\n </div>\n </div>\n </template>\n\n <!-- Alternating layout -->\n <template v-else>\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n class=\"flex items-stretch\"\n :class=\"i % 2 === 0 ? 'flex-row' : 'flex-row-reverse'\"\n >\n <!-- Content side -->\n <div\n class=\"flex-1\"\n :class=\"[i % 2 === 0 ? 'text-right' : 'text-left', dense ? 'pb-4' : 'pb-8']\"\n >\n <p class=\"text-body-large font-medium text-on-surface\">{{ item.title }}</p>\n <p v-if=\"item.description\" class=\"mt-1 text-body-medium text-on-surface-variant\">\n {{ item.description }}\n </p>\n <span\n v-if=\"item.date\"\n class=\"mt-1 inline-block text-label-small text-on-surface-variant\"\n >{{ item.date }}</span\n >\n </div>\n\n <!-- Center column: dot + continuous line -->\n <div class=\"flex w-14 shrink-0 flex-col items-center\">\n <div\n class=\"z-[1] flex shrink-0 items-center justify-center rounded-full\"\n :class=\"[item.icon ? 'h-9 w-9' : 'h-3.5 w-3.5', dotBg[item.color ?? 'primary']]\"\n :style=\"item.dotColor ? { backgroundColor: item.dotColor } : undefined\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n </div>\n <div v-if=\"i < items.length - 1\" class=\"w-[2px] flex-1 bg-outline-variant\" />\n </div>\n\n <!-- Empty side -->\n <div class=\"flex-1\" />\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from \"./MIcon.vue\";\n\nexport interface TimelineItem {\n title: string;\n description?: string;\n date?: string;\n icon?: string;\n color?: \"primary\" | \"secondary\" | \"tertiary\" | \"error\" | \"success\";\n dotColor?: string;\n}\n\nwithDefaults(\n defineProps<{\n items: TimelineItem[];\n dense?: boolean;\n alternating?: boolean;\n }>(),\n { dense: false, alternating: false },\n);\n\nconst dotBg: Record<string, string> = {\n primary: \"bg-primary text-on-primary\",\n secondary: \"bg-secondary text-on-secondary\",\n tertiary: \"bg-tertiary text-on-tertiary\",\n error: \"bg-error text-on-error\",\n success: \"bg-success text-on-success\",\n};\n</script>\n\n<template>\n <div :class=\"alternating ? 'relative' : 'flex flex-col'\">\n <!-- Standard layout -->\n <template v-if=\"!alternating\">\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n class=\"relative flex gap-4\"\n :class=\"dense ? 'pb-4' : 'pb-8'\"\n >\n <!-- Line + dot -->\n <div class=\"flex flex-col items-center\">\n <div\n class=\"z-[1] flex shrink-0 items-center justify-center rounded-full\"\n :class=\"[item.icon ? 'h-9 w-9' : 'h-3 w-3', dotBg[item.color ?? 'primary']]\"\n :style=\"item.dotColor ? { backgroundColor: item.dotColor } : undefined\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n </div>\n <div\n v-if=\"i < items.length - 1\"\n class=\"w-[2px] flex-1\"\n :class=\"dotBg[item.color ?? 'primary']!.split(' ')[0] + '/30'\"\n style=\"min-height: 16px\"\n />\n </div>\n\n <!-- Content -->\n <div :class=\"item.icon ? '' : 'pt-0'\" class=\"-mt-0.5 flex-1\">\n <div class=\"flex items-baseline justify-between gap-2\">\n <p class=\"text-body-large font-medium text-on-surface\">{{ item.title }}</p>\n <span v-if=\"item.date\" class=\"shrink-0 text-label-small text-on-surface-variant\">{{\n item.date\n }}</span>\n </div>\n <p v-if=\"item.description\" class=\"mt-1 text-body-medium text-on-surface-variant\">\n {{ item.description }}\n </p>\n <div v-if=\"$slots[`item-${i}`]\" class=\"mt-2\">\n <slot :name=\"`item-${i}`\" :item=\"item\" />\n </div>\n </div>\n </div>\n </template>\n\n <!-- Alternating layout -->\n <template v-else>\n <div\n v-for=\"(item, i) in items\"\n :key=\"i\"\n class=\"flex items-stretch\"\n :class=\"i % 2 === 0 ? 'flex-row' : 'flex-row-reverse'\"\n >\n <!-- Content side -->\n <div\n class=\"flex-1\"\n :class=\"[i % 2 === 0 ? 'text-right' : 'text-left', dense ? 'pb-4' : 'pb-8']\"\n >\n <p class=\"text-body-large font-medium text-on-surface\">{{ item.title }}</p>\n <p v-if=\"item.description\" class=\"mt-1 text-body-medium text-on-surface-variant\">\n {{ item.description }}\n </p>\n <span\n v-if=\"item.date\"\n class=\"mt-1 inline-block text-label-small text-on-surface-variant\"\n >{{ item.date }}</span\n >\n </div>\n\n <!-- Center column: dot + continuous line -->\n <div class=\"flex w-14 shrink-0 flex-col items-center\">\n <div\n class=\"z-[1] flex shrink-0 items-center justify-center rounded-full\"\n :class=\"[item.icon ? 'h-9 w-9' : 'h-3.5 w-3.5', dotBg[item.color ?? 'primary']]\"\n :style=\"item.dotColor ? { backgroundColor: item.dotColor } : undefined\"\n >\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" />\n </div>\n <div v-if=\"i < items.length - 1\" class=\"w-[2px] flex-1 bg-outline-variant\" />\n </div>\n\n <!-- Empty side -->\n <div class=\"flex-1\" />\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string | null\n label?: string\n disabled?: boolean\n error?: string\n hint?: string\n minuteStep?: number\n use24h?: boolean\n fieldBg?: string\n}>(), { minuteStep: 5, use24h: true })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | null] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst mode = ref<'hour' | 'minute'>('hour')\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst parsed = computed(() => {\n if (!props.modelValue) return { h: 12, m: 0 }\n const parts = props.modelValue.split(':').map(Number)\n return { h: parts[0] ?? 12, m: parts[1] ?? 0 }\n})\n\nconst selectedHour = ref(parsed.value.h)\nconst selectedMinute = ref(parsed.value.m)\nwatch(() => props.modelValue, () => {\n selectedHour.value = parsed.value.h\n selectedMinute.value = parsed.value.m\n})\n\nconst hours = Array.from({ length: 24 }, (_, i) => i)\nconst minutes = computed(() => {\n const arr: number[] = []\n for (let m = 0; m < 60; m += props.minuteStep) arr.push(m)\n return arr\n})\n\nfunction pad(n: number) { return String(n).padStart(2, '0') }\n\nfunction selectHour(h: number) {\n selectedHour.value = h\n mode.value = 'minute'\n}\n\nfunction selectMinute(m: number) {\n selectedMinute.value = m\n emit('update:modelValue', `${pad(selectedHour.value)}:${pad(m)}`)\n open.value = false\n mode.value = 'hour'\n}\n\nfunction clear() {\n emit('update:modelValue', null)\n}\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n return `${pad(parsed.value.h)}:${pad(parsed.value.m)}`\n})\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 320\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n mode.value = 'hour'\n selectedHour.value = parsed.value.h\n selectedMinute.value = parsed.value.m\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"schedule\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 font-mono text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ label || 'Seleccionar hora' }}</span>\n <MIcon\n v-if=\"modelValue\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[280px] rounded-lg bg-surface-container shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Display -->\n <div class=\"flex items-center justify-center gap-1 border-b border-outline-variant px-4 py-4\">\n <button\n type=\"button\"\n class=\"rounded-lg px-3 py-2 font-mono text-headline-medium transition-colors\"\n :class=\"mode === 'hour' ? 'bg-primary-container text-on-primary-container cursor-default' : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"mode = 'hour'\"\n >\n {{ pad(selectedHour) }}\n </button>\n <span class=\"text-headline-medium text-on-surface-variant\">:</span>\n <button\n type=\"button\"\n class=\"rounded-lg px-3 py-2 font-mono text-headline-medium transition-colors\"\n :class=\"mode === 'minute' ? 'bg-primary-container text-on-primary-container cursor-default' : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"mode = 'minute'\"\n >\n {{ pad(selectedMinute) }}\n </button>\n </div>\n\n <!-- Grid -->\n <div class=\"p-3\">\n <div v-if=\"mode === 'hour'\" class=\"grid grid-cols-6 gap-1\">\n <button\n v-for=\"h in hours\"\n :key=\"h\"\n type=\"button\"\n class=\"flex h-9 cursor-pointer items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"\n h === selectedHour\n ? 'bg-primary text-on-primary'\n : 'text-on-surface hover:bg-on-surface/8'\n \"\n @click=\"selectHour(h)\"\n >\n {{ pad(h) }}\n </button>\n </div>\n\n <div v-else class=\"grid grid-cols-6 gap-1\">\n <button\n v-for=\"m in minutes\"\n :key=\"m\"\n type=\"button\"\n class=\"flex h-9 cursor-pointer items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"\n m === selectedMinute\n ? 'bg-primary text-on-primary'\n : 'text-on-surface hover:bg-on-surface/8'\n \"\n @click=\"selectMinute(m)\"\n >\n {{ pad(m) }}\n </button>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport MIcon from './MIcon.vue'\nimport { useFieldBg } from '../composables/useFieldBg'\n\nconst props = withDefaults(defineProps<{\n modelValue: string | null\n label?: string\n disabled?: boolean\n error?: string\n hint?: string\n minuteStep?: number\n use24h?: boolean\n fieldBg?: string\n}>(), { minuteStep: 5, use24h: true })\n\nconst emit = defineEmits<{ 'update:modelValue': [string | null] }>()\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst mode = ref<'hour' | 'minute'>('hour')\nconst dropPos = ref({ top: '0px', left: '0px' })\nconst { resolvedFieldBg } = useFieldBg(triggerEl, () => props.fieldBg)\n\nconst parsed = computed(() => {\n if (!props.modelValue) return { h: 12, m: 0 }\n const parts = props.modelValue.split(':').map(Number)\n return { h: parts[0] ?? 12, m: parts[1] ?? 0 }\n})\n\nconst selectedHour = ref(parsed.value.h)\nconst selectedMinute = ref(parsed.value.m)\nwatch(() => props.modelValue, () => {\n selectedHour.value = parsed.value.h\n selectedMinute.value = parsed.value.m\n})\n\nconst hours = Array.from({ length: 24 }, (_, i) => i)\nconst minutes = computed(() => {\n const arr: number[] = []\n for (let m = 0; m < 60; m += props.minuteStep) arr.push(m)\n return arr\n})\n\nfunction pad(n: number) { return String(n).padStart(2, '0') }\n\nfunction selectHour(h: number) {\n selectedHour.value = h\n mode.value = 'minute'\n}\n\nfunction selectMinute(m: number) {\n selectedMinute.value = m\n emit('update:modelValue', `${pad(selectedHour.value)}:${pad(m)}`)\n open.value = false\n mode.value = 'hour'\n}\n\nfunction clear() {\n emit('update:modelValue', null)\n}\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n return `${pad(parsed.value.h)}:${pad(parsed.value.m)}`\n})\n\nfunction computeDropPos() {\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n const panelH = 320\n const spaceBelow = window.innerHeight - rect.bottom - 8\n const above = spaceBelow < panelH && rect.top > panelH\n dropPos.value = {\n top: above ? `${rect.top - 4 - panelH}px` : `${rect.bottom + 4}px`,\n left: `${rect.left}px`,\n }\n}\n\nfunction onOut(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (panelEl.value?.contains(t)) return\n open.value = false\n}\n\nfunction onScroll(e: Event) {\n if (!open.value) return\n if (panelEl.value?.contains(e.target as Node)) return\n if (!triggerEl.value) return\n const rect = triggerEl.value.getBoundingClientRect()\n if (rect.bottom < 0 || rect.top > window.innerHeight) { open.value = false; return }\n computeDropPos()\n}\n\nwatch(open, (v) => {\n if (v) {\n mode.value = 'hour'\n selectedHour.value = parsed.value.h\n selectedMinute.value = parsed.value.m\n computeDropPos()\n setTimeout(() => document.addEventListener('mousedown', onOut), 0)\n } else {\n document.removeEventListener('mousedown', onOut)\n }\n})\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => {\n window.removeEventListener('scroll', onScroll, true)\n document.removeEventListener('mousedown', onOut)\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1\">\n <div ref=\"triggerEl\" class=\"relative mt-2\" :style=\"{ '--field-bg': resolvedFieldBg }\">\n <button\n type=\"button\"\n class=\"flex h-14 w-full items-center gap-2 rounded-sm border bg-transparent px-4 text-left text-body-large transition-[border-color,border-width] duration-150\"\n :class=\"[\n disabled ? 'pointer-events-none opacity-[0.38]' : 'cursor-pointer',\n open\n ? error ? 'border-2 border-error' : 'border-2 border-primary'\n : error ? 'border-error' : 'border-outline hover:border-on-surface',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <MIcon name=\"schedule\" :size=\"20\" class=\"shrink-0 text-on-surface-variant\" />\n <span v-if=\"displayValue\" class=\"flex-1 font-mono text-on-surface\">{{ displayValue }}</span>\n <span v-else class=\"flex-1 text-on-surface-variant\">{{ label || 'Seleccionar hora' }}</span>\n <MIcon\n v-if=\"modelValue\"\n name=\"close\"\n :size=\"18\"\n class=\"shrink-0 cursor-pointer text-on-surface-variant hover:text-on-surface\"\n @click.stop=\"clear\"\n />\n </button>\n <label\n v-if=\"label\"\n class=\"pointer-events-none absolute -top-2.5 left-3 bg-[var(--field-bg)] px-1 text-label-small transition-colors\"\n :class=\"open ? (error ? 'text-error' : 'text-primary') : error ? 'text-error' : 'text-on-surface-variant'\"\n >\n {{ label }}\n </label>\n </div>\n\n <p v-if=\"error\" class=\"px-4 text-body-small text-error\">{{ error }}</p>\n <p v-else-if=\"hint\" class=\"px-4 text-body-small text-on-surface-variant\">{{ hint }}</p>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-[opacity,transform] duration-150\"\n enter-from-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n leave-active-class=\"transition-[opacity,transform] duration-100\"\n leave-to-class=\"opacity-0 -translate-y-1 scale-[0.98]\"\n >\n <div\n v-if=\"open\"\n ref=\"panelEl\"\n class=\"fixed z-[500] w-[280px] rounded-lg bg-surface-container shadow-elevation-3\"\n :style=\"dropPos\"\n >\n <!-- Display -->\n <div class=\"flex items-center justify-center gap-1 border-b border-outline-variant px-4 py-4\">\n <button\n type=\"button\"\n class=\"rounded-lg px-3 py-2 font-mono text-headline-medium transition-colors\"\n :class=\"mode === 'hour' ? 'bg-primary-container text-on-primary-container cursor-default' : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"mode = 'hour'\"\n >\n {{ pad(selectedHour) }}\n </button>\n <span class=\"text-headline-medium text-on-surface-variant\">:</span>\n <button\n type=\"button\"\n class=\"rounded-lg px-3 py-2 font-mono text-headline-medium transition-colors\"\n :class=\"mode === 'minute' ? 'bg-primary-container text-on-primary-container cursor-default' : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8'\"\n @click=\"mode = 'minute'\"\n >\n {{ pad(selectedMinute) }}\n </button>\n </div>\n\n <!-- Grid -->\n <div class=\"p-3\">\n <div v-if=\"mode === 'hour'\" class=\"grid grid-cols-6 gap-1\">\n <button\n v-for=\"h in hours\"\n :key=\"h\"\n type=\"button\"\n class=\"flex h-9 cursor-pointer items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"\n h === selectedHour\n ? 'bg-primary text-on-primary'\n : 'text-on-surface hover:bg-on-surface/8'\n \"\n @click=\"selectHour(h)\"\n >\n {{ pad(h) }}\n </button>\n </div>\n\n <div v-else class=\"grid grid-cols-6 gap-1\">\n <button\n v-for=\"m in minutes\"\n :key=\"m\"\n type=\"button\"\n class=\"flex h-9 cursor-pointer items-center justify-center rounded-full text-body-medium transition-colors duration-100\"\n :class=\"\n m === selectedMinute\n ? 'bg-primary text-on-primary'\n : 'text-on-surface hover:bg-on-surface/8'\n \"\n @click=\"selectMinute(m)\"\n >\n {{ pad(m) }}\n </button>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, onUnmounted, ref } from 'vue'\n\nconst props = withDefaults(defineProps<{\n text: string\n placement?: 'top' | 'bottom' | 'left' | 'right'\n delay?: number\n}>(), { placement: 'top', delay: 600 })\n\nconst visible = ref(false)\nconst tipEl = ref<HTMLElement>()\nconst triggerEl = ref<HTMLElement>()\nconst tipStyle = ref<Record<string, string>>({})\nlet timer: ReturnType<typeof setTimeout> | null = null\n\nasync function show() {\n if (timer) clearTimeout(timer)\n timer = setTimeout(async () => {\n visible.value = true\n await nextTick()\n reposition()\n }, props.delay)\n}\n\nfunction hide() {\n if (timer) { clearTimeout(timer); timer = null }\n visible.value = false\n}\n\nfunction onScroll() {\n if (visible.value) hide()\n}\n\nfunction reposition() {\n if (!triggerEl.value || !tipEl.value) return\n const tr = triggerEl.value.getBoundingClientRect()\n const tt = tipEl.value.getBoundingClientRect()\n const GAP = 6\n\n let top = 0, left = 0\n switch (props.placement) {\n case 'top': top = tr.top - tt.height - GAP; left = tr.left + (tr.width - tt.width) / 2; break\n case 'bottom': top = tr.bottom + GAP; left = tr.left + (tr.width - tt.width) / 2; break\n case 'left': top = tr.top + (tr.height - tt.height) / 2; left = tr.left - tt.width - GAP; break\n case 'right': top = tr.top + (tr.height - tt.height) / 2; left = tr.right + GAP; break\n }\n\n top = Math.max(6, Math.min(top, window.innerHeight - tt.height - 6))\n left = Math.max(6, Math.min(left, window.innerWidth - tt.width - 6))\n tipStyle.value = { top: `${top}px`, left: `${left}px` }\n}\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => window.removeEventListener('scroll', onScroll, true))\n</script>\n\n<template>\n <span\n ref=\"triggerEl\"\n class=\"inline-flex\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n >\n <slot />\n </span>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-150\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-100\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible && text\"\n ref=\"tipEl\"\n class=\"pointer-events-none fixed z-[400] max-w-[220px] rounded bg-inverse-surface px-3 py-1.5 text-label-medium text-inverse-on-surface shadow-elevation-2\"\n :style=\"tipStyle\"\n role=\"tooltip\"\n >\n {{ text }}\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, onUnmounted, ref } from 'vue'\n\nconst props = withDefaults(defineProps<{\n text: string\n placement?: 'top' | 'bottom' | 'left' | 'right'\n delay?: number\n}>(), { placement: 'top', delay: 600 })\n\nconst visible = ref(false)\nconst tipEl = ref<HTMLElement>()\nconst triggerEl = ref<HTMLElement>()\nconst tipStyle = ref<Record<string, string>>({})\nlet timer: ReturnType<typeof setTimeout> | null = null\n\nasync function show() {\n if (timer) clearTimeout(timer)\n timer = setTimeout(async () => {\n visible.value = true\n await nextTick()\n reposition()\n }, props.delay)\n}\n\nfunction hide() {\n if (timer) { clearTimeout(timer); timer = null }\n visible.value = false\n}\n\nfunction onScroll() {\n if (visible.value) hide()\n}\n\nfunction reposition() {\n if (!triggerEl.value || !tipEl.value) return\n const tr = triggerEl.value.getBoundingClientRect()\n const tt = tipEl.value.getBoundingClientRect()\n const GAP = 6\n\n let top = 0, left = 0\n switch (props.placement) {\n case 'top': top = tr.top - tt.height - GAP; left = tr.left + (tr.width - tt.width) / 2; break\n case 'bottom': top = tr.bottom + GAP; left = tr.left + (tr.width - tt.width) / 2; break\n case 'left': top = tr.top + (tr.height - tt.height) / 2; left = tr.left - tt.width - GAP; break\n case 'right': top = tr.top + (tr.height - tt.height) / 2; left = tr.right + GAP; break\n }\n\n top = Math.max(6, Math.min(top, window.innerHeight - tt.height - 6))\n left = Math.max(6, Math.min(left, window.innerWidth - tt.width - 6))\n tipStyle.value = { top: `${top}px`, left: `${left}px` }\n}\n\nonMounted(() => window.addEventListener('scroll', onScroll, true))\nonUnmounted(() => window.removeEventListener('scroll', onScroll, true))\n</script>\n\n<template>\n <span\n ref=\"triggerEl\"\n class=\"inline-flex\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n >\n <slot />\n </span>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-150\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-100\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"visible && text\"\n ref=\"tipEl\"\n class=\"pointer-events-none fixed z-[400] max-w-[220px] rounded bg-inverse-surface px-3 py-1.5 text-label-medium text-inverse-on-surface shadow-elevation-2\"\n :style=\"tipStyle\"\n role=\"tooltip\"\n >\n {{ text }}\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nwithDefaults(defineProps<{\n title?: string\n variant?: 'center' | 'small' | 'medium' | 'large'\n navigationIcon?: string\n elevated?: boolean\n}>(), { variant: 'small' })\n\ndefineEmits<{ navigation: [] }>()\n</script>\n\n<template>\n <header\n class=\"flex w-full flex-col bg-surface transition-shadow\"\n :class=\"elevated ? 'shadow-elevation-2' : ''\"\n >\n <!-- Top row -->\n <div class=\"flex h-16 items-center gap-1 px-2\">\n <!-- Navigation icon -->\n <MIconButton\n v-if=\"navigationIcon\"\n :icon=\"navigationIcon\"\n label=\"Navegación\"\n @click=\"$emit('navigation')\"\n />\n\n <!-- Title: center or small variant -->\n <h1\n v-if=\"variant === 'center' || variant === 'small'\"\n class=\"flex-1 truncate px-2 text-title-large text-on-surface\"\n :class=\"variant === 'center' ? 'text-center' : ''\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n\n <!-- Spacer for medium/large (title is below) -->\n <div v-else class=\"flex-1\" />\n\n <!-- Trailing actions -->\n <div v-if=\"$slots.actions\" class=\"flex items-center gap-1\">\n <slot name=\"actions\" />\n </div>\n </div>\n\n <!-- Large title row for medium/large variants -->\n <div\n v-if=\"variant === 'medium' || variant === 'large'\"\n class=\"px-4 pb-6\"\n :class=\"variant === 'large' ? 'pt-4' : 'pt-1'\"\n >\n <h1\n class=\"text-on-surface\"\n :class=\"variant === 'large' ? 'text-headline-medium' : 'text-headline-small'\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\n\nwithDefaults(defineProps<{\n title?: string\n variant?: 'center' | 'small' | 'medium' | 'large'\n navigationIcon?: string\n elevated?: boolean\n}>(), { variant: 'small' })\n\ndefineEmits<{ navigation: [] }>()\n</script>\n\n<template>\n <header\n class=\"flex w-full flex-col bg-surface transition-shadow\"\n :class=\"elevated ? 'shadow-elevation-2' : ''\"\n >\n <!-- Top row -->\n <div class=\"flex h-16 items-center gap-1 px-2\">\n <!-- Navigation icon -->\n <MIconButton\n v-if=\"navigationIcon\"\n :icon=\"navigationIcon\"\n label=\"Navegación\"\n @click=\"$emit('navigation')\"\n />\n\n <!-- Title: center or small variant -->\n <h1\n v-if=\"variant === 'center' || variant === 'small'\"\n class=\"flex-1 truncate px-2 text-title-large text-on-surface\"\n :class=\"variant === 'center' ? 'text-center' : ''\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n\n <!-- Spacer for medium/large (title is below) -->\n <div v-else class=\"flex-1\" />\n\n <!-- Trailing actions -->\n <div v-if=\"$slots.actions\" class=\"flex items-center gap-1\">\n <slot name=\"actions\" />\n </div>\n </div>\n\n <!-- Large title row for medium/large variants -->\n <div\n v-if=\"variant === 'medium' || variant === 'large'\"\n class=\"px-4 pb-6\"\n :class=\"variant === 'large' ? 'pt-4' : 'pt-1'\"\n >\n <h1\n class=\"text-on-surface\"\n :class=\"variant === 'large' ? 'text-headline-medium' : 'text-headline-small'\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onBeforeUnmount } from 'vue'\nimport MButton from './MButton.vue'\nimport MIcon from './MIcon.vue'\n\nexport interface TourStep {\n target: string\n title: string\n content: string\n placement?: 'top' | 'bottom' | 'left' | 'right'\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n steps: TourStep[]\n }>(),\n {},\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n finish: []\n}>()\n\nconst currentStep = ref(0)\nconst tooltipStyle = ref<Record<string, string>>({})\nconst arrowStyle = ref<Record<string, string>>({})\nconst placement = ref<'top' | 'bottom' | 'left' | 'right'>('bottom')\n\nconst step = computed(() => props.steps[currentStep.value])\nconst isFirst = computed(() => currentStep.value === 0)\nconst isLast = computed(() => currentStep.value === props.steps.length - 1)\n\nfunction positionTooltip() {\n if (!step.value) return\n const el = document.querySelector(step.value.target) as HTMLElement | null\n if (!el) return\n\n const rect = el.getBoundingClientRect()\n const pad = 12\n const arrowSize = 8\n const p = step.value.placement ?? 'bottom'\n placement.value = p\n\n el.scrollIntoView({ behavior: 'smooth', block: 'center' })\n\n const s: Record<string, string> = {}\n const a: Record<string, string> = { position: 'absolute' }\n\n switch (p) {\n case 'bottom':\n s.top = `${rect.bottom + pad}px`\n s.left = `${rect.left + rect.width / 2}px`\n s.transform = 'translateX(-50%)'\n a.top = `-${arrowSize}px`\n a.left = '50%'\n a.transform = 'translateX(-50%)'\n a.borderBottom = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderLeft = `${arrowSize}px solid transparent`\n a.borderRight = `${arrowSize}px solid transparent`\n break\n case 'top':\n s.bottom = `${window.innerHeight - rect.top + pad}px`\n s.left = `${rect.left + rect.width / 2}px`\n s.transform = 'translateX(-50%)'\n a.bottom = `-${arrowSize}px`\n a.left = '50%'\n a.transform = 'translateX(-50%)'\n a.borderTop = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderLeft = `${arrowSize}px solid transparent`\n a.borderRight = `${arrowSize}px solid transparent`\n break\n case 'left':\n s.top = `${rect.top + rect.height / 2}px`\n s.right = `${window.innerWidth - rect.left + pad}px`\n s.transform = 'translateY(-50%)'\n a.top = '50%'\n a.right = `-${arrowSize}px`\n a.transform = 'translateY(-50%)'\n a.borderLeft = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderTop = `${arrowSize}px solid transparent`\n a.borderBottom = `${arrowSize}px solid transparent`\n break\n case 'right':\n s.top = `${rect.top + rect.height / 2}px`\n s.left = `${rect.right + pad}px`\n s.transform = 'translateY(-50%)'\n a.top = '50%'\n a.left = `-${arrowSize}px`\n a.transform = 'translateY(-50%)'\n a.borderRight = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderTop = `${arrowSize}px solid transparent`\n a.borderBottom = `${arrowSize}px solid transparent`\n break\n }\n\n tooltipStyle.value = s\n arrowStyle.value = a\n}\n\nfunction highlightTarget() {\n if (!step.value) return\n document.querySelectorAll('.m3-tour-highlight').forEach((el) => el.classList.remove('m3-tour-highlight'))\n const el = document.querySelector(step.value.target)\n el?.classList.add('m3-tour-highlight')\n}\n\nfunction clearHighlight() {\n document.querySelectorAll('.m3-tour-highlight').forEach((el) => el.classList.remove('m3-tour-highlight'))\n}\n\nfunction goNext() {\n if (isLast.value) {\n close()\n emit('finish')\n } else {\n currentStep.value++\n }\n}\n\nfunction goPrev() {\n if (!isFirst.value) currentStep.value--\n}\n\nfunction close() {\n clearHighlight()\n currentStep.value = 0\n emit('update:modelValue', false)\n}\n\nwatch([() => props.modelValue, currentStep], () => {\n if (props.modelValue) {\n nextTick(() => {\n highlightTarget()\n positionTooltip()\n })\n }\n})\n\nwatch(() => props.modelValue, (v) => {\n if (!v) clearHighlight()\n})\n\nonBeforeUnmount(clearHighlight)\n</script>\n\n<template>\n <Teleport to=\"body\">\n <!-- Overlay -->\n <Transition name=\"m3-tour\">\n <div v-if=\"modelValue && step\" class=\"fixed inset-0 z-[200] bg-black/40\" @click=\"close\" />\n </Transition>\n\n <!-- Tooltip -->\n <Transition name=\"m3-tour\">\n <div\n v-if=\"modelValue && step\"\n class=\"fixed z-[202] w-80 rounded-xl bg-surface-container-high p-5 shadow-elevation-3\"\n :style=\"tooltipStyle\"\n >\n <!-- Arrow -->\n <div class=\"h-0 w-0\" :style=\"arrowStyle\" />\n\n <!-- Step indicator -->\n <div class=\"mb-2 flex items-center justify-between\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ currentStep + 1 }} / {{ steps.length }}\n </span>\n <button\n type=\"button\"\n class=\"flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"16\" />\n </button>\n </div>\n\n <h3 class=\"mb-1 text-title-medium font-medium text-on-surface\">{{ step.title }}</h3>\n <p class=\"mb-4 text-body-medium text-on-surface-variant\">{{ step.content }}</p>\n\n <!-- Progress dots -->\n <div class=\"mb-4 flex justify-center gap-1.5\">\n <div\n v-for=\"(_, i) in steps\"\n :key=\"i\"\n class=\"h-1.5 rounded-full transition-all duration-200\"\n :class=\"i === currentStep ? 'w-6 bg-primary' : 'w-1.5 bg-outline-variant'\"\n />\n </div>\n\n <!-- Actions -->\n <div class=\"flex justify-between\">\n <MButton\n v-if=\"!isFirst\"\n variant=\"text\"\n @click=\"goPrev\"\n >\n Anterior\n </MButton>\n <span v-else />\n <MButton @click=\"goNext\">\n {{ isLast ? 'Finalizar' : 'Siguiente' }}\n </MButton>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style>\n.m3-tour-highlight {\n position: relative;\n z-index: 201 !important;\n box-shadow: 0 0 0 4px var(--color-primary);\n border-radius: 8px;\n}\n\n.m3-tour-enter-active,\n.m3-tour-leave-active {\n transition: opacity 0.2s ease;\n}\n.m3-tour-enter-from,\n.m3-tour-leave-to {\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick, onBeforeUnmount } from 'vue'\nimport MButton from './MButton.vue'\nimport MIcon from './MIcon.vue'\n\nexport interface TourStep {\n target: string\n title: string\n content: string\n placement?: 'top' | 'bottom' | 'left' | 'right'\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n steps: TourStep[]\n }>(),\n {},\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [boolean]\n finish: []\n}>()\n\nconst currentStep = ref(0)\nconst tooltipStyle = ref<Record<string, string>>({})\nconst arrowStyle = ref<Record<string, string>>({})\nconst placement = ref<'top' | 'bottom' | 'left' | 'right'>('bottom')\n\nconst step = computed(() => props.steps[currentStep.value])\nconst isFirst = computed(() => currentStep.value === 0)\nconst isLast = computed(() => currentStep.value === props.steps.length - 1)\n\nfunction positionTooltip() {\n if (!step.value) return\n const el = document.querySelector(step.value.target) as HTMLElement | null\n if (!el) return\n\n const rect = el.getBoundingClientRect()\n const pad = 12\n const arrowSize = 8\n const p = step.value.placement ?? 'bottom'\n placement.value = p\n\n el.scrollIntoView({ behavior: 'smooth', block: 'center' })\n\n const s: Record<string, string> = {}\n const a: Record<string, string> = { position: 'absolute' }\n\n switch (p) {\n case 'bottom':\n s.top = `${rect.bottom + pad}px`\n s.left = `${rect.left + rect.width / 2}px`\n s.transform = 'translateX(-50%)'\n a.top = `-${arrowSize}px`\n a.left = '50%'\n a.transform = 'translateX(-50%)'\n a.borderBottom = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderLeft = `${arrowSize}px solid transparent`\n a.borderRight = `${arrowSize}px solid transparent`\n break\n case 'top':\n s.bottom = `${window.innerHeight - rect.top + pad}px`\n s.left = `${rect.left + rect.width / 2}px`\n s.transform = 'translateX(-50%)'\n a.bottom = `-${arrowSize}px`\n a.left = '50%'\n a.transform = 'translateX(-50%)'\n a.borderTop = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderLeft = `${arrowSize}px solid transparent`\n a.borderRight = `${arrowSize}px solid transparent`\n break\n case 'left':\n s.top = `${rect.top + rect.height / 2}px`\n s.right = `${window.innerWidth - rect.left + pad}px`\n s.transform = 'translateY(-50%)'\n a.top = '50%'\n a.right = `-${arrowSize}px`\n a.transform = 'translateY(-50%)'\n a.borderLeft = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderTop = `${arrowSize}px solid transparent`\n a.borderBottom = `${arrowSize}px solid transparent`\n break\n case 'right':\n s.top = `${rect.top + rect.height / 2}px`\n s.left = `${rect.right + pad}px`\n s.transform = 'translateY(-50%)'\n a.top = '50%'\n a.left = `-${arrowSize}px`\n a.transform = 'translateY(-50%)'\n a.borderRight = `${arrowSize}px solid var(--color-surface-container-high)`\n a.borderTop = `${arrowSize}px solid transparent`\n a.borderBottom = `${arrowSize}px solid transparent`\n break\n }\n\n tooltipStyle.value = s\n arrowStyle.value = a\n}\n\nfunction highlightTarget() {\n if (!step.value) return\n document.querySelectorAll('.m3-tour-highlight').forEach((el) => el.classList.remove('m3-tour-highlight'))\n const el = document.querySelector(step.value.target)\n el?.classList.add('m3-tour-highlight')\n}\n\nfunction clearHighlight() {\n document.querySelectorAll('.m3-tour-highlight').forEach((el) => el.classList.remove('m3-tour-highlight'))\n}\n\nfunction goNext() {\n if (isLast.value) {\n close()\n emit('finish')\n } else {\n currentStep.value++\n }\n}\n\nfunction goPrev() {\n if (!isFirst.value) currentStep.value--\n}\n\nfunction close() {\n clearHighlight()\n currentStep.value = 0\n emit('update:modelValue', false)\n}\n\nwatch([() => props.modelValue, currentStep], () => {\n if (props.modelValue) {\n nextTick(() => {\n highlightTarget()\n positionTooltip()\n })\n }\n})\n\nwatch(() => props.modelValue, (v) => {\n if (!v) clearHighlight()\n})\n\nonBeforeUnmount(clearHighlight)\n</script>\n\n<template>\n <Teleport to=\"body\">\n <!-- Overlay -->\n <Transition name=\"m3-tour\">\n <div v-if=\"modelValue && step\" class=\"fixed inset-0 z-[200] bg-black/40\" @click=\"close\" />\n </Transition>\n\n <!-- Tooltip -->\n <Transition name=\"m3-tour\">\n <div\n v-if=\"modelValue && step\"\n class=\"fixed z-[202] w-80 rounded-xl bg-surface-container-high p-5 shadow-elevation-3\"\n :style=\"tooltipStyle\"\n >\n <!-- Arrow -->\n <div class=\"h-0 w-0\" :style=\"arrowStyle\" />\n\n <!-- Step indicator -->\n <div class=\"mb-2 flex items-center justify-between\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ currentStep + 1 }} / {{ steps.length }}\n </span>\n <button\n type=\"button\"\n class=\"flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8\"\n @click=\"close\"\n >\n <MIcon name=\"close\" :size=\"16\" />\n </button>\n </div>\n\n <h3 class=\"mb-1 text-title-medium font-medium text-on-surface\">{{ step.title }}</h3>\n <p class=\"mb-4 text-body-medium text-on-surface-variant\">{{ step.content }}</p>\n\n <!-- Progress dots -->\n <div class=\"mb-4 flex justify-center gap-1.5\">\n <div\n v-for=\"(_, i) in steps\"\n :key=\"i\"\n class=\"h-1.5 rounded-full transition-all duration-200\"\n :class=\"i === currentStep ? 'w-6 bg-primary' : 'w-1.5 bg-outline-variant'\"\n />\n </div>\n\n <!-- Actions -->\n <div class=\"flex justify-between\">\n <MButton\n v-if=\"!isFirst\"\n variant=\"text\"\n @click=\"goPrev\"\n >\n Anterior\n </MButton>\n <span v-else />\n <MButton @click=\"goNext\">\n {{ isLast ? 'Finalizar' : 'Siguiente' }}\n </MButton>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style>\n.m3-tour-highlight {\n position: relative;\n z-index: 201 !important;\n box-shadow: 0 0 0 4px var(--color-primary);\n border-radius: 8px;\n}\n\n.m3-tour-enter-active,\n.m3-tour-leave-active {\n transition: opacity 0.2s ease;\n}\n.m3-tour-enter-from,\n.m3-tour-leave-to {\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MCheckbox from './MCheckbox.vue'\n\nexport interface TransferItem {\n value: string | number\n label: string\n icon?: string\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: (string | number)[]\n items: TransferItem[]\n sourceTitle?: string\n targetTitle?: string\n filterable?: boolean\n}>(), { sourceTitle: 'Disponibles', targetTitle: 'Seleccionados', filterable: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [(string | number)[]] }>()\n\nconst checkedSource = ref<Set<string | number>>(new Set())\nconst checkedTarget = ref<Set<string | number>>(new Set())\nconst sourceSearch = ref('')\nconst targetSearch = ref('')\n\nconst sourceItems = computed(() => {\n const selected = new Set(props.modelValue)\n let list = props.items.filter(i => !selected.has(i.value))\n if (sourceSearch.value) {\n const q = sourceSearch.value.toLowerCase()\n list = list.filter(i => i.label.toLowerCase().includes(q))\n }\n return list\n})\n\nconst targetItems = computed(() => {\n const selected = new Set(props.modelValue)\n let list = props.items.filter(i => selected.has(i.value))\n if (targetSearch.value) {\n const q = targetSearch.value.toLowerCase()\n list = list.filter(i => i.label.toLowerCase().includes(q))\n }\n return list\n})\n\nfunction toggleSource(value: string | number) {\n const s = new Set(checkedSource.value)\n s.has(value) ? s.delete(value) : s.add(value)\n checkedSource.value = s\n}\nfunction toggleTarget(value: string | number) {\n const s = new Set(checkedTarget.value)\n s.has(value) ? s.delete(value) : s.add(value)\n checkedTarget.value = s\n}\n\nfunction moveRight() {\n const next = [...props.modelValue, ...checkedSource.value]\n emit('update:modelValue', next)\n checkedSource.value = new Set()\n}\nfunction moveLeft() {\n const remove = checkedTarget.value\n emit('update:modelValue', props.modelValue.filter(v => !remove.has(v)))\n checkedTarget.value = new Set()\n}\nfunction moveAllRight() {\n const all = sourceItems.value.map(i => i.value)\n emit('update:modelValue', [...props.modelValue, ...all])\n checkedSource.value = new Set()\n}\nfunction moveAllLeft() {\n const keep = targetItems.value.map(i => i.value)\n emit('update:modelValue', props.modelValue.filter(v => !new Set(keep).has(v)))\n checkedTarget.value = new Set()\n}\n</script>\n\n<template>\n <div class=\"flex items-stretch gap-2\">\n <!-- Source list -->\n <div class=\"flex min-w-0 flex-1 flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-3 py-2\">\n <span class=\"text-label-large font-medium text-on-surface\">{{ sourceTitle }}</span>\n <span class=\"text-label-small text-on-surface-variant\">{{ sourceItems.length }}</span>\n </div>\n <div v-if=\"filterable\" class=\"border-b border-outline-variant px-3 py-2\">\n <input\n v-model=\"sourceSearch\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n />\n </div>\n <div class=\"flex-1 overflow-y-auto\" style=\"max-height: 240px\">\n <button\n v-for=\"item in sourceItems\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-on-surface/4\"\n @click=\"toggleSource(item.value)\"\n >\n <MCheckbox :model-value=\"checkedSource.has(item.value)\" @update:model-value=\"toggleSource(item.value)\" />\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n <span class=\"flex-1 truncate text-body-medium text-on-surface\">{{ item.label }}</span>\n </button>\n <p v-if=\"!sourceItems.length\" class=\"px-3 py-4 text-center text-body-small text-on-surface-variant\">\n Sin elementos\n </p>\n </div>\n </div>\n\n <!-- Transfer buttons -->\n <div class=\"flex flex-col items-center justify-center gap-1\">\n <MIconButton\n icon=\"keyboard_double_arrow_right\"\n label=\"Mover todos a la derecha\"\n :size=\"36\"\n :disabled=\"!sourceItems.length\"\n @click=\"moveAllRight\"\n />\n <MIconButton\n icon=\"chevron_right\"\n label=\"Mover seleccionados a la derecha\"\n variant=\"tonal\"\n :size=\"36\"\n :disabled=\"!checkedSource.size\"\n @click=\"moveRight\"\n />\n <MIconButton\n icon=\"chevron_left\"\n label=\"Mover seleccionados a la izquierda\"\n variant=\"tonal\"\n :size=\"36\"\n :disabled=\"!checkedTarget.size\"\n @click=\"moveLeft\"\n />\n <MIconButton\n icon=\"keyboard_double_arrow_left\"\n label=\"Mover todos a la izquierda\"\n :size=\"36\"\n :disabled=\"!targetItems.length\"\n @click=\"moveAllLeft\"\n />\n </div>\n\n <!-- Target list -->\n <div class=\"flex min-w-0 flex-1 flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-3 py-2\">\n <span class=\"text-label-large font-medium text-on-surface\">{{ targetTitle }}</span>\n <span class=\"text-label-small text-on-surface-variant\">{{ targetItems.length }}</span>\n </div>\n <div v-if=\"filterable\" class=\"border-b border-outline-variant px-3 py-2\">\n <input\n v-model=\"targetSearch\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n />\n </div>\n <div class=\"flex-1 overflow-y-auto\" style=\"max-height: 240px\">\n <button\n v-for=\"item in targetItems\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-on-surface/4\"\n @click=\"toggleTarget(item.value)\"\n >\n <MCheckbox :model-value=\"checkedTarget.has(item.value)\" @update:model-value=\"toggleTarget(item.value)\" />\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n <span class=\"flex-1 truncate text-body-medium text-on-surface\">{{ item.label }}</span>\n </button>\n <p v-if=\"!targetItems.length\" class=\"px-3 py-4 text-center text-body-small text-on-surface-variant\">\n Sin elementos\n </p>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\nimport MIconButton from './MIconButton.vue'\nimport MCheckbox from './MCheckbox.vue'\n\nexport interface TransferItem {\n value: string | number\n label: string\n icon?: string\n}\n\nconst props = withDefaults(defineProps<{\n modelValue: (string | number)[]\n items: TransferItem[]\n sourceTitle?: string\n targetTitle?: string\n filterable?: boolean\n}>(), { sourceTitle: 'Disponibles', targetTitle: 'Seleccionados', filterable: false })\n\nconst emit = defineEmits<{ 'update:modelValue': [(string | number)[]] }>()\n\nconst checkedSource = ref<Set<string | number>>(new Set())\nconst checkedTarget = ref<Set<string | number>>(new Set())\nconst sourceSearch = ref('')\nconst targetSearch = ref('')\n\nconst sourceItems = computed(() => {\n const selected = new Set(props.modelValue)\n let list = props.items.filter(i => !selected.has(i.value))\n if (sourceSearch.value) {\n const q = sourceSearch.value.toLowerCase()\n list = list.filter(i => i.label.toLowerCase().includes(q))\n }\n return list\n})\n\nconst targetItems = computed(() => {\n const selected = new Set(props.modelValue)\n let list = props.items.filter(i => selected.has(i.value))\n if (targetSearch.value) {\n const q = targetSearch.value.toLowerCase()\n list = list.filter(i => i.label.toLowerCase().includes(q))\n }\n return list\n})\n\nfunction toggleSource(value: string | number) {\n const s = new Set(checkedSource.value)\n s.has(value) ? s.delete(value) : s.add(value)\n checkedSource.value = s\n}\nfunction toggleTarget(value: string | number) {\n const s = new Set(checkedTarget.value)\n s.has(value) ? s.delete(value) : s.add(value)\n checkedTarget.value = s\n}\n\nfunction moveRight() {\n const next = [...props.modelValue, ...checkedSource.value]\n emit('update:modelValue', next)\n checkedSource.value = new Set()\n}\nfunction moveLeft() {\n const remove = checkedTarget.value\n emit('update:modelValue', props.modelValue.filter(v => !remove.has(v)))\n checkedTarget.value = new Set()\n}\nfunction moveAllRight() {\n const all = sourceItems.value.map(i => i.value)\n emit('update:modelValue', [...props.modelValue, ...all])\n checkedSource.value = new Set()\n}\nfunction moveAllLeft() {\n const keep = targetItems.value.map(i => i.value)\n emit('update:modelValue', props.modelValue.filter(v => !new Set(keep).has(v)))\n checkedTarget.value = new Set()\n}\n</script>\n\n<template>\n <div class=\"flex items-stretch gap-2\">\n <!-- Source list -->\n <div class=\"flex min-w-0 flex-1 flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-3 py-2\">\n <span class=\"text-label-large font-medium text-on-surface\">{{ sourceTitle }}</span>\n <span class=\"text-label-small text-on-surface-variant\">{{ sourceItems.length }}</span>\n </div>\n <div v-if=\"filterable\" class=\"border-b border-outline-variant px-3 py-2\">\n <input\n v-model=\"sourceSearch\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n />\n </div>\n <div class=\"flex-1 overflow-y-auto\" style=\"max-height: 240px\">\n <button\n v-for=\"item in sourceItems\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-on-surface/4\"\n @click=\"toggleSource(item.value)\"\n >\n <MCheckbox :model-value=\"checkedSource.has(item.value)\" @update:model-value=\"toggleSource(item.value)\" />\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n <span class=\"flex-1 truncate text-body-medium text-on-surface\">{{ item.label }}</span>\n </button>\n <p v-if=\"!sourceItems.length\" class=\"px-3 py-4 text-center text-body-small text-on-surface-variant\">\n Sin elementos\n </p>\n </div>\n </div>\n\n <!-- Transfer buttons -->\n <div class=\"flex flex-col items-center justify-center gap-1\">\n <MIconButton\n icon=\"keyboard_double_arrow_right\"\n label=\"Mover todos a la derecha\"\n :size=\"36\"\n :disabled=\"!sourceItems.length\"\n @click=\"moveAllRight\"\n />\n <MIconButton\n icon=\"chevron_right\"\n label=\"Mover seleccionados a la derecha\"\n variant=\"tonal\"\n :size=\"36\"\n :disabled=\"!checkedSource.size\"\n @click=\"moveRight\"\n />\n <MIconButton\n icon=\"chevron_left\"\n label=\"Mover seleccionados a la izquierda\"\n variant=\"tonal\"\n :size=\"36\"\n :disabled=\"!checkedTarget.size\"\n @click=\"moveLeft\"\n />\n <MIconButton\n icon=\"keyboard_double_arrow_left\"\n label=\"Mover todos a la izquierda\"\n :size=\"36\"\n :disabled=\"!targetItems.length\"\n @click=\"moveAllLeft\"\n />\n </div>\n\n <!-- Target list -->\n <div class=\"flex min-w-0 flex-1 flex-col overflow-hidden rounded-lg border border-outline-variant\">\n <div class=\"flex items-center justify-between border-b border-outline-variant bg-surface-container px-3 py-2\">\n <span class=\"text-label-large font-medium text-on-surface\">{{ targetTitle }}</span>\n <span class=\"text-label-small text-on-surface-variant\">{{ targetItems.length }}</span>\n </div>\n <div v-if=\"filterable\" class=\"border-b border-outline-variant px-3 py-2\">\n <input\n v-model=\"targetSearch\"\n type=\"text\"\n placeholder=\"Buscar...\"\n class=\"w-full bg-transparent text-body-medium text-on-surface outline-none placeholder:text-on-surface-variant/50\"\n />\n </div>\n <div class=\"flex-1 overflow-y-auto\" style=\"max-height: 240px\">\n <button\n v-for=\"item in targetItems\"\n :key=\"item.value\"\n type=\"button\"\n class=\"flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-on-surface/4\"\n @click=\"toggleTarget(item.value)\"\n >\n <MCheckbox :model-value=\"checkedTarget.has(item.value)\" @update:model-value=\"toggleTarget(item.value)\" />\n <MIcon v-if=\"item.icon\" :name=\"item.icon\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n <span class=\"flex-1 truncate text-body-medium text-on-surface\">{{ item.label }}</span>\n </button>\n <p v-if=\"!targetItems.length\" class=\"px-3 py-4 text-center text-body-small text-on-surface-variant\">\n Sin elementos\n </p>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from 'vue'\nimport MTreeNode from './_MTreeNode.vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport type { TreeContext, TreeNode } from './MTree.vue'\n\nconst props = defineProps<{ node: TreeNode; depth: number }>()\n\nconst tree = inject<TreeContext>('m-tree')!\n\nconst hasChildren = computed(() => !!props.node.children?.length)\nconst isExpanded = computed(() => tree.expandedIds.value.has(props.node.id))\nconst isSelected = computed(() => tree.selected.value === props.node.id)\n\n// Use only leaf ids for checkbox visual state (branch ids are never stored in checkedSet)\nconst leafIds = computed(() => tree.getLeafIds(props.node))\nconst checkedLeafCount = computed(() => leafIds.value.filter(id => tree.checkedSet.value.has(id)).length)\nconst isChecked = computed(() => leafIds.value.length > 0 && checkedLeafCount.value === leafIds.value.length)\nconst isIndeterminate = computed(() => checkedLeafCount.value > 0 && !isChecked.value)\n\nfunction onRowClick() {\n if (props.node.disabled) return\n tree.selectNode(props.node)\n if (hasChildren.value) tree.toggleExpand(props.node.id)\n}\n\nfunction onChevronClick(e: MouseEvent) {\n e.stopPropagation()\n if (props.node.disabled) return\n tree.toggleExpand(props.node.id)\n}\n\nfunction onCheck() {\n if (props.node.disabled) return\n tree.toggleCheck(props.node)\n}\n\n// ── Height transition hooks ────────────────────────────────────────────────\nfunction onEnter(el: Element) {\n const e = el as HTMLElement\n e.style.height = '0'\n e.style.opacity = '0'\n e.style.overflow = 'hidden'\n e.offsetHeight // force reflow\n e.style.transition = 'height 200ms cubic-bezier(0.4,0,0.2,1), opacity 150ms ease'\n e.style.height = e.scrollHeight + 'px'\n e.style.opacity = '1'\n}\nfunction onAfterEnter(el: Element) {\n const e = el as HTMLElement\n e.style.height = ''\n e.style.overflow = ''\n e.style.transition = ''\n e.style.opacity = ''\n}\nfunction onLeave(el: Element) {\n const e = el as HTMLElement\n e.style.height = e.scrollHeight + 'px'\n e.style.overflow = 'hidden'\n e.offsetHeight // force reflow\n e.style.transition = 'height 180ms cubic-bezier(0.4,0,0.2,1), opacity 120ms ease'\n e.style.height = '0'\n e.style.opacity = '0'\n}\nfunction onAfterLeave(el: Element) {\n const e = el as HTMLElement\n e.style.height = ''\n e.style.overflow = ''\n e.style.transition = ''\n e.style.opacity = ''\n}\n</script>\n\n<template>\n <div role=\"treeitem\" :aria-expanded=\"hasChildren ? isExpanded : undefined\" :aria-selected=\"isSelected\">\n <!-- ── Row ─────────────────────────────────────────────────────────────── -->\n <div\n :class=\"[\n 'flex items-center gap-1 rounded-sm py-1 pr-2 transition-colors duration-100 select-none',\n node.disabled\n ? 'cursor-not-allowed opacity-38'\n : 'cursor-pointer',\n !node.disabled && isSelected\n ? 'bg-primary/[0.10]'\n : !node.disabled\n ? 'hover:bg-on-surface/[0.04]'\n : '',\n ]\"\n @click=\"onRowClick\"\n >\n <!-- Indent spacer (no lines for depth 0, lines provided by parent's border-l) -->\n <div class=\"flex w-6 shrink-0 items-center justify-center\">\n <!-- Chevron for branch nodes -->\n <button\n v-if=\"hasChildren\"\n type=\"button\"\n class=\"flex h-5 w-5 items-center justify-center rounded text-on-surface-variant transition-transform duration-200\"\n :class=\"isExpanded ? 'rotate-90' : ''\"\n :disabled=\"node.disabled || undefined\"\n @click=\"onChevronClick\"\n >\n <MIcon name=\"chevron_right\" :size=\"16\" />\n </button>\n </div>\n\n <!-- Checkbox (checkable mode) -->\n <div v-if=\"tree.checkable.value\" class=\"shrink-0\" @click.stop=\"onCheck\">\n <MCheckbox\n :model-value=\"isChecked\"\n :indeterminate=\"isIndeterminate\"\n :disabled=\"node.disabled\"\n @update:model-value=\"onCheck\"\n />\n </div>\n\n <!-- Node icon -->\n <MIcon\n v-if=\"node.icon\"\n :name=\"node.icon\"\n :size=\"16\"\n class=\"shrink-0 transition-colors\"\n :class=\"isSelected ? 'text-primary' : 'text-on-surface-variant'\"\n />\n\n <!-- Label -->\n <span\n class=\"min-w-0 flex-1 truncate text-body-medium transition-colors\"\n :class=\"isSelected ? 'font-medium text-primary' : 'text-on-surface'\"\n >\n <slot name=\"label\" :node=\"node\">{{ node.label }}</slot>\n </span>\n\n <!-- Check count badge (branch + checkable) -->\n <span\n v-if=\"hasChildren && tree.checkable.value\"\n class=\"shrink-0 text-label-small tabular-nums text-on-surface-variant\"\n >\n {{ checkedLeafCount }}/{{ leafIds.length }}\n </span>\n\n <!-- Optional trailing slot -->\n <slot name=\"trailing\" :node=\"node\" />\n </div>\n\n <!-- ── Children ───────────────────────────────────────────────────────── -->\n <Transition\n @enter=\"onEnter\"\n @after-enter=\"onAfterEnter\"\n @leave=\"onLeave\"\n @after-leave=\"onAfterLeave\"\n >\n <div\n v-if=\"isExpanded && hasChildren\"\n class=\"ml-3 border-l border-outline-variant pl-2\"\n >\n <MTreeNode\n v-for=\"child in node.children\"\n :key=\"child.id\"\n :node=\"child\"\n :depth=\"depth + 1\"\n >\n <!-- @vue-ignore -->\n <template v-for=\"(_, name) in $slots\" #[name]=\"sp\">\n <slot :name=\"name\" v-bind=\"sp ?? {}\" />\n </template>\n </MTreeNode>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from 'vue'\nimport MTreeNode from './_MTreeNode.vue'\nimport MCheckbox from './MCheckbox.vue'\nimport MIcon from './MIcon.vue'\nimport type { TreeContext, TreeNode } from './MTree.vue'\n\nconst props = defineProps<{ node: TreeNode; depth: number }>()\n\nconst tree = inject<TreeContext>('m-tree')!\n\nconst hasChildren = computed(() => !!props.node.children?.length)\nconst isExpanded = computed(() => tree.expandedIds.value.has(props.node.id))\nconst isSelected = computed(() => tree.selected.value === props.node.id)\n\n// Use only leaf ids for checkbox visual state (branch ids are never stored in checkedSet)\nconst leafIds = computed(() => tree.getLeafIds(props.node))\nconst checkedLeafCount = computed(() => leafIds.value.filter(id => tree.checkedSet.value.has(id)).length)\nconst isChecked = computed(() => leafIds.value.length > 0 && checkedLeafCount.value === leafIds.value.length)\nconst isIndeterminate = computed(() => checkedLeafCount.value > 0 && !isChecked.value)\n\nfunction onRowClick() {\n if (props.node.disabled) return\n tree.selectNode(props.node)\n if (hasChildren.value) tree.toggleExpand(props.node.id)\n}\n\nfunction onChevronClick(e: MouseEvent) {\n e.stopPropagation()\n if (props.node.disabled) return\n tree.toggleExpand(props.node.id)\n}\n\nfunction onCheck() {\n if (props.node.disabled) return\n tree.toggleCheck(props.node)\n}\n\n// ── Height transition hooks ────────────────────────────────────────────────\nfunction onEnter(el: Element) {\n const e = el as HTMLElement\n e.style.height = '0'\n e.style.opacity = '0'\n e.style.overflow = 'hidden'\n e.offsetHeight // force reflow\n e.style.transition = 'height 200ms cubic-bezier(0.4,0,0.2,1), opacity 150ms ease'\n e.style.height = e.scrollHeight + 'px'\n e.style.opacity = '1'\n}\nfunction onAfterEnter(el: Element) {\n const e = el as HTMLElement\n e.style.height = ''\n e.style.overflow = ''\n e.style.transition = ''\n e.style.opacity = ''\n}\nfunction onLeave(el: Element) {\n const e = el as HTMLElement\n e.style.height = e.scrollHeight + 'px'\n e.style.overflow = 'hidden'\n e.offsetHeight // force reflow\n e.style.transition = 'height 180ms cubic-bezier(0.4,0,0.2,1), opacity 120ms ease'\n e.style.height = '0'\n e.style.opacity = '0'\n}\nfunction onAfterLeave(el: Element) {\n const e = el as HTMLElement\n e.style.height = ''\n e.style.overflow = ''\n e.style.transition = ''\n e.style.opacity = ''\n}\n</script>\n\n<template>\n <div role=\"treeitem\" :aria-expanded=\"hasChildren ? isExpanded : undefined\" :aria-selected=\"isSelected\">\n <!-- ── Row ─────────────────────────────────────────────────────────────── -->\n <div\n :class=\"[\n 'flex items-center gap-1 rounded-sm py-1 pr-2 transition-colors duration-100 select-none',\n node.disabled\n ? 'cursor-not-allowed opacity-38'\n : 'cursor-pointer',\n !node.disabled && isSelected\n ? 'bg-primary/[0.10]'\n : !node.disabled\n ? 'hover:bg-on-surface/[0.04]'\n : '',\n ]\"\n @click=\"onRowClick\"\n >\n <!-- Indent spacer (no lines for depth 0, lines provided by parent's border-l) -->\n <div class=\"flex w-6 shrink-0 items-center justify-center\">\n <!-- Chevron for branch nodes -->\n <button\n v-if=\"hasChildren\"\n type=\"button\"\n class=\"flex h-5 w-5 items-center justify-center rounded text-on-surface-variant transition-transform duration-200\"\n :class=\"isExpanded ? 'rotate-90' : ''\"\n :disabled=\"node.disabled || undefined\"\n @click=\"onChevronClick\"\n >\n <MIcon name=\"chevron_right\" :size=\"16\" />\n </button>\n </div>\n\n <!-- Checkbox (checkable mode) -->\n <div v-if=\"tree.checkable.value\" class=\"shrink-0\" @click.stop=\"onCheck\">\n <MCheckbox\n :model-value=\"isChecked\"\n :indeterminate=\"isIndeterminate\"\n :disabled=\"node.disabled\"\n @update:model-value=\"onCheck\"\n />\n </div>\n\n <!-- Node icon -->\n <MIcon\n v-if=\"node.icon\"\n :name=\"node.icon\"\n :size=\"16\"\n class=\"shrink-0 transition-colors\"\n :class=\"isSelected ? 'text-primary' : 'text-on-surface-variant'\"\n />\n\n <!-- Label -->\n <span\n class=\"min-w-0 flex-1 truncate text-body-medium transition-colors\"\n :class=\"isSelected ? 'font-medium text-primary' : 'text-on-surface'\"\n >\n <slot name=\"label\" :node=\"node\">{{ node.label }}</slot>\n </span>\n\n <!-- Check count badge (branch + checkable) -->\n <span\n v-if=\"hasChildren && tree.checkable.value\"\n class=\"shrink-0 text-label-small tabular-nums text-on-surface-variant\"\n >\n {{ checkedLeafCount }}/{{ leafIds.length }}\n </span>\n\n <!-- Optional trailing slot -->\n <slot name=\"trailing\" :node=\"node\" />\n </div>\n\n <!-- ── Children ───────────────────────────────────────────────────────── -->\n <Transition\n @enter=\"onEnter\"\n @after-enter=\"onAfterEnter\"\n @leave=\"onLeave\"\n @after-leave=\"onAfterLeave\"\n >\n <div\n v-if=\"isExpanded && hasChildren\"\n class=\"ml-3 border-l border-outline-variant pl-2\"\n >\n <MTreeNode\n v-for=\"child in node.children\"\n :key=\"child.id\"\n :node=\"child\"\n :depth=\"depth + 1\"\n >\n <!-- @vue-ignore -->\n <template v-for=\"(_, name) in $slots\" #[name]=\"sp\">\n <slot :name=\"name\" v-bind=\"sp ?? {}\" />\n </template>\n </MTreeNode>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide, ref, type Ref } from 'vue'\nimport MTreeNode from './_MTreeNode.vue'\nimport MIcon from './MIcon.vue'\n\n// ── Public types ────────────────────────────────────────────────────────────\n\nexport interface TreeNode {\n id: string | number\n label: string\n icon?: string\n children?: TreeNode[]\n disabled?: boolean\n [key: string]: unknown\n}\n\n/** Shape injected into every _MTreeNode via provide/inject. */\nexport interface TreeContext {\n selected: Ref<string | number | null>\n checkedSet: Ref<Set<string | number>>\n expandedIds: Ref<Set<string | number>>\n checkable: Ref<boolean>\n selectNode: (node: TreeNode) => void\n toggleExpand: (id: string | number) => void\n toggleCheck: (node: TreeNode) => void\n getDescendantIds: (node: TreeNode) => (string | number)[]\n getLeafIds: (node: TreeNode) => (string | number)[]\n}\n\n// ── Props & emits ───────────────────────────────────────────────────────────\n\nconst props = withDefaults(\n defineProps<{\n nodes: TreeNode[]\n /** Currently selected node id (single-select). */\n selected?: string | number | null\n /** Checked node ids (checkable multi-select). */\n checked?: (string | number)[]\n /** Show checkboxes with cascade selection. */\n checkable?: boolean\n /**\n * Which nodes start expanded.\n * 'all' | 'none' | array of ids (default: 'none').\n */\n defaultExpanded?: (string | number)[] | 'all' | 'none'\n emptyText?: string\n }>(),\n {\n selected: null,\n checked: () => [],\n checkable: false,\n defaultExpanded: 'none',\n emptyText: 'Sin elementos',\n },\n)\n\nconst emit = defineEmits<{\n 'update:selected': [string | number | null]\n 'update:checked': [(string | number)[]]\n 'node-click': [TreeNode]\n}>()\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction getDescendantIds(node: TreeNode): (string | number)[] {\n return [node.id, ...(node.children ?? []).flatMap(getDescendantIds)]\n}\n\nfunction getLeafIds(node: TreeNode): (string | number)[] {\n if (!node.children?.length) return [node.id]\n return node.children.flatMap(getLeafIds)\n}\n\nfunction getAllIds(nodes: TreeNode[]): (string | number)[] {\n return nodes.flatMap((n) => getDescendantIds(n))\n}\n\n// ── Expand state ────────────────────────────────────────────────────────────\n\nfunction buildInitialExpanded(): Set<string | number> {\n if (props.defaultExpanded === 'all') return new Set(getAllIds(props.nodes))\n if (props.defaultExpanded === 'none') return new Set()\n return new Set(props.defaultExpanded)\n}\n\nconst expandedIds = ref<Set<string | number>>(buildInitialExpanded())\n\nfunction toggleExpand(id: string | number) {\n const next = new Set(expandedIds.value)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n expandedIds.value = next\n}\n\n// ── Selection ───────────────────────────────────────────────────────────────\n\nconst selectedRef = computed(() => props.selected ?? null)\n\nfunction selectNode(node: TreeNode) {\n emit('update:selected', selectedRef.value === node.id ? null : node.id)\n emit('node-click', node)\n}\n\n// ── Checkable ───────────────────────────────────────────────────────────────\n\nconst checkedSet = computed(() => new Set(props.checked))\n\nfunction toggleCheck(node: TreeNode) {\n const leafIds = getLeafIds(node)\n const allLeafsChecked = leafIds.every((id) => checkedSet.value.has(id))\n const next = new Set(props.checked)\n if (allLeafsChecked) {\n // Remove leaf ids + clean up any stale branch ids\n getDescendantIds(node).forEach((id) => next.delete(id))\n } else {\n leafIds.forEach((id) => next.add(id))\n }\n emit('update:checked', [...next])\n}\n\n// ── Provide context ─────────────────────────────────────────────────────────\n\nprovide<TreeContext>('m-tree', {\n selected: selectedRef,\n checkedSet,\n expandedIds,\n checkable: computed(() => props.checkable),\n selectNode,\n toggleExpand,\n toggleCheck,\n getDescendantIds,\n getLeafIds,\n})\n\n// ── Expose expand/collapse utilities ────────────────────────────────────────\n\nfunction expandAll() { expandedIds.value = new Set(getAllIds(props.nodes)) }\nfunction collapseAll() { expandedIds.value = new Set() }\n\ndefineExpose({ expandAll, collapseAll })\n</script>\n\n<template>\n <div role=\"tree\" class=\"flex flex-col\">\n <template v-if=\"nodes.length\">\n <MTreeNode\n v-for=\"node in nodes\"\n :key=\"node.id\"\n :node=\"node\"\n :depth=\"0\"\n >\n <!-- Forward all slots down the recursive tree -->\n <template v-for=\"(_, name) in $slots\" #[name]=\"slotProps\">\n <slot :name=\"name\" v-bind=\"slotProps ?? {}\" />\n </template>\n </MTreeNode>\n </template>\n\n <div v-else class=\"flex flex-col items-center gap-2 py-10 text-on-surface-variant\">\n <MIcon name=\"account_tree\" :size=\"32\" class=\"opacity-30\" />\n <p class=\"text-body-medium\">{{ emptyText }}</p>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide, ref, type Ref } from 'vue'\nimport MTreeNode from './_MTreeNode.vue'\nimport MIcon from './MIcon.vue'\n\n// ── Public types ────────────────────────────────────────────────────────────\n\nexport interface TreeNode {\n id: string | number\n label: string\n icon?: string\n children?: TreeNode[]\n disabled?: boolean\n [key: string]: unknown\n}\n\n/** Shape injected into every _MTreeNode via provide/inject. */\nexport interface TreeContext {\n selected: Ref<string | number | null>\n checkedSet: Ref<Set<string | number>>\n expandedIds: Ref<Set<string | number>>\n checkable: Ref<boolean>\n selectNode: (node: TreeNode) => void\n toggleExpand: (id: string | number) => void\n toggleCheck: (node: TreeNode) => void\n getDescendantIds: (node: TreeNode) => (string | number)[]\n getLeafIds: (node: TreeNode) => (string | number)[]\n}\n\n// ── Props & emits ───────────────────────────────────────────────────────────\n\nconst props = withDefaults(\n defineProps<{\n nodes: TreeNode[]\n /** Currently selected node id (single-select). */\n selected?: string | number | null\n /** Checked node ids (checkable multi-select). */\n checked?: (string | number)[]\n /** Show checkboxes with cascade selection. */\n checkable?: boolean\n /**\n * Which nodes start expanded.\n * 'all' | 'none' | array of ids (default: 'none').\n */\n defaultExpanded?: (string | number)[] | 'all' | 'none'\n emptyText?: string\n }>(),\n {\n selected: null,\n checked: () => [],\n checkable: false,\n defaultExpanded: 'none',\n emptyText: 'Sin elementos',\n },\n)\n\nconst emit = defineEmits<{\n 'update:selected': [string | number | null]\n 'update:checked': [(string | number)[]]\n 'node-click': [TreeNode]\n}>()\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction getDescendantIds(node: TreeNode): (string | number)[] {\n return [node.id, ...(node.children ?? []).flatMap(getDescendantIds)]\n}\n\nfunction getLeafIds(node: TreeNode): (string | number)[] {\n if (!node.children?.length) return [node.id]\n return node.children.flatMap(getLeafIds)\n}\n\nfunction getAllIds(nodes: TreeNode[]): (string | number)[] {\n return nodes.flatMap((n) => getDescendantIds(n))\n}\n\n// ── Expand state ────────────────────────────────────────────────────────────\n\nfunction buildInitialExpanded(): Set<string | number> {\n if (props.defaultExpanded === 'all') return new Set(getAllIds(props.nodes))\n if (props.defaultExpanded === 'none') return new Set()\n return new Set(props.defaultExpanded)\n}\n\nconst expandedIds = ref<Set<string | number>>(buildInitialExpanded())\n\nfunction toggleExpand(id: string | number) {\n const next = new Set(expandedIds.value)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n expandedIds.value = next\n}\n\n// ── Selection ───────────────────────────────────────────────────────────────\n\nconst selectedRef = computed(() => props.selected ?? null)\n\nfunction selectNode(node: TreeNode) {\n emit('update:selected', selectedRef.value === node.id ? null : node.id)\n emit('node-click', node)\n}\n\n// ── Checkable ───────────────────────────────────────────────────────────────\n\nconst checkedSet = computed(() => new Set(props.checked))\n\nfunction toggleCheck(node: TreeNode) {\n const leafIds = getLeafIds(node)\n const allLeafsChecked = leafIds.every((id) => checkedSet.value.has(id))\n const next = new Set(props.checked)\n if (allLeafsChecked) {\n // Remove leaf ids + clean up any stale branch ids\n getDescendantIds(node).forEach((id) => next.delete(id))\n } else {\n leafIds.forEach((id) => next.add(id))\n }\n emit('update:checked', [...next])\n}\n\n// ── Provide context ─────────────────────────────────────────────────────────\n\nprovide<TreeContext>('m-tree', {\n selected: selectedRef,\n checkedSet,\n expandedIds,\n checkable: computed(() => props.checkable),\n selectNode,\n toggleExpand,\n toggleCheck,\n getDescendantIds,\n getLeafIds,\n})\n\n// ── Expose expand/collapse utilities ────────────────────────────────────────\n\nfunction expandAll() { expandedIds.value = new Set(getAllIds(props.nodes)) }\nfunction collapseAll() { expandedIds.value = new Set() }\n\ndefineExpose({ expandAll, collapseAll })\n</script>\n\n<template>\n <div role=\"tree\" class=\"flex flex-col\">\n <template v-if=\"nodes.length\">\n <MTreeNode\n v-for=\"node in nodes\"\n :key=\"node.id\"\n :node=\"node\"\n :depth=\"0\"\n >\n <!-- Forward all slots down the recursive tree -->\n <template v-for=\"(_, name) in $slots\" #[name]=\"slotProps\">\n <slot :name=\"name\" v-bind=\"slotProps ?? {}\" />\n </template>\n </MTreeNode>\n </template>\n\n <div v-else class=\"flex flex-col items-center gap-2 py-10 text-on-surface-variant\">\n <MIcon name=\"account_tree\" :size=\"32\" class=\"opacity-30\" />\n <p class=\"text-body-medium\">{{ emptyText }}</p>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface TreeTableColumn {\n key: string\n label: string\n width?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport interface TreeTableRow {\n [key: string]: any\n children?: TreeTableRow[]\n}\n\nconst props = withDefaults(defineProps<{\n columns: TreeTableColumn[]\n rows: TreeTableRow[]\n rowKey?: string\n defaultExpanded?: boolean\n indent?: number\n dense?: boolean\n}>(), {\n rowKey: 'id',\n defaultExpanded: false,\n indent: 24,\n dense: false,\n})\n\nconst emit = defineEmits<{ rowClick: [TreeTableRow] }>()\n\nconst expanded = ref<Set<any>>(new Set(\n props.defaultExpanded ? collectIds(props.rows) : []\n))\n\nfunction collectIds(rows: TreeTableRow[]): any[] {\n const ids: any[] = []\n for (const r of rows) {\n if (r.children?.length) {\n ids.push(r[props.rowKey])\n ids.push(...collectIds(r.children))\n }\n }\n return ids\n}\n\nfunction toggleExpand(row: TreeTableRow) {\n const id = row[props.rowKey]\n const next = new Set(expanded.value)\n next.has(id) ? next.delete(id) : next.add(id)\n expanded.value = next\n}\n\nfunction isExpanded(row: TreeTableRow) { return expanded.value.has(row[props.rowKey]) }\n\ninterface FlatRow {\n row: TreeTableRow\n depth: number\n hasChildren: boolean\n isExpanded: boolean\n}\n\nconst flatRows = computed(() => {\n const result: FlatRow[] = []\n function walk(rows: TreeTableRow[], depth: number) {\n for (const row of rows) {\n const hasChildren = !!row.children?.length\n const exp = isExpanded(row)\n result.push({ row, depth, hasChildren, isExpanded: exp })\n if (hasChildren && exp) walk(row.children!, depth + 1)\n }\n }\n walk(props.rows, 0)\n return result\n})\n\nfunction expandAll() {\n expanded.value = new Set(collectIds(props.rows))\n}\nfunction collapseAll() {\n expanded.value = new Set()\n}\n\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n <!-- Toolbar -->\n <div v-if=\"$slots.toolbar\" class=\"flex items-center gap-2 border-b border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <slot name=\"toolbar\" :expand-all=\"expandAll\" :collapse-all=\"collapseAll\" />\n </div>\n\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead>\n <tr class=\"bg-surface-container-high\">\n <th\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n :style=\"col.width ? { width: col.width } : undefined\"\n :class=\"[\n 'whitespace-nowrap text-label-medium font-medium text-on-surface-variant',\n dense ? 'px-3 py-2' : 'px-4 py-3',\n alignClass(col.align),\n ]\"\n >\n {{ col.label }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(item, i) in flatRows\"\n :key=\"item.row[rowKey] ?? i\"\n class=\"border-t border-outline-variant transition-colors duration-100 hover:bg-on-surface/[0.04]\"\n :class=\"item.depth > 0 ? 'bg-surface-container-lowest/50' : ''\"\n @click=\"emit('rowClick', item.row)\"\n >\n <td\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n :class=\"['text-body-medium text-on-surface', alignClass(col.align), dense ? 'px-3 py-1.5' : 'px-4 py-3']\"\n >\n <!-- First column gets indent + expand icon -->\n <div v-if=\"ci === 0\" class=\"flex items-center gap-1\" :style=\"{ paddingLeft: `${item.depth * indent}px` }\">\n <button\n v-if=\"item.hasChildren\"\n type=\"button\"\n class=\"flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-transform duration-200 hover:bg-on-surface/8\"\n :class=\"item.isExpanded ? 'rotate-90' : ''\"\n @click.stop=\"toggleExpand(item.row)\"\n >\n <MIcon name=\"chevron_right\" :size=\"18\" />\n </button>\n <span v-else class=\"w-6 shrink-0\" />\n <slot :name=\"`cell-${col.key}`\" :row=\"item.row\" :value=\"item.row[col.key]\" :depth=\"item.depth\">\n <span :class=\"item.hasChildren ? 'font-medium' : ''\">{{ item.row[col.key] ?? '—' }}</span>\n </slot>\n </div>\n <template v-else>\n <slot :name=\"`cell-${col.key}`\" :row=\"item.row\" :value=\"item.row[col.key]\" :depth=\"item.depth\">\n {{ item.row[col.key] ?? '—' }}\n </slot>\n </template>\n </td>\n </tr>\n <tr v-if=\"!flatRows.length\">\n <td :colspan=\"columns.length\" class=\"border-t border-outline-variant px-4 py-10 text-center\">\n <MIcon name=\"account_tree\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">Sin datos</p>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface TreeTableColumn {\n key: string\n label: string\n width?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport interface TreeTableRow {\n [key: string]: any\n children?: TreeTableRow[]\n}\n\nconst props = withDefaults(defineProps<{\n columns: TreeTableColumn[]\n rows: TreeTableRow[]\n rowKey?: string\n defaultExpanded?: boolean\n indent?: number\n dense?: boolean\n}>(), {\n rowKey: 'id',\n defaultExpanded: false,\n indent: 24,\n dense: false,\n})\n\nconst emit = defineEmits<{ rowClick: [TreeTableRow] }>()\n\nconst expanded = ref<Set<any>>(new Set(\n props.defaultExpanded ? collectIds(props.rows) : []\n))\n\nfunction collectIds(rows: TreeTableRow[]): any[] {\n const ids: any[] = []\n for (const r of rows) {\n if (r.children?.length) {\n ids.push(r[props.rowKey])\n ids.push(...collectIds(r.children))\n }\n }\n return ids\n}\n\nfunction toggleExpand(row: TreeTableRow) {\n const id = row[props.rowKey]\n const next = new Set(expanded.value)\n next.has(id) ? next.delete(id) : next.add(id)\n expanded.value = next\n}\n\nfunction isExpanded(row: TreeTableRow) { return expanded.value.has(row[props.rowKey]) }\n\ninterface FlatRow {\n row: TreeTableRow\n depth: number\n hasChildren: boolean\n isExpanded: boolean\n}\n\nconst flatRows = computed(() => {\n const result: FlatRow[] = []\n function walk(rows: TreeTableRow[], depth: number) {\n for (const row of rows) {\n const hasChildren = !!row.children?.length\n const exp = isExpanded(row)\n result.push({ row, depth, hasChildren, isExpanded: exp })\n if (hasChildren && exp) walk(row.children!, depth + 1)\n }\n }\n walk(props.rows, 0)\n return result\n})\n\nfunction expandAll() {\n expanded.value = new Set(collectIds(props.rows))\n}\nfunction collapseAll() {\n expanded.value = new Set()\n}\n\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n <!-- Toolbar -->\n <div v-if=\"$slots.toolbar\" class=\"flex items-center gap-2 border-b border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <slot name=\"toolbar\" :expand-all=\"expandAll\" :collapse-all=\"collapseAll\" />\n </div>\n\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead>\n <tr class=\"bg-surface-container-high\">\n <th\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n :style=\"col.width ? { width: col.width } : undefined\"\n :class=\"[\n 'whitespace-nowrap text-label-medium font-medium text-on-surface-variant',\n dense ? 'px-3 py-2' : 'px-4 py-3',\n alignClass(col.align),\n ]\"\n >\n {{ col.label }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(item, i) in flatRows\"\n :key=\"item.row[rowKey] ?? i\"\n class=\"border-t border-outline-variant transition-colors duration-100 hover:bg-on-surface/[0.04]\"\n :class=\"item.depth > 0 ? 'bg-surface-container-lowest/50' : ''\"\n @click=\"emit('rowClick', item.row)\"\n >\n <td\n v-for=\"(col, ci) in columns\"\n :key=\"col.key\"\n :class=\"['text-body-medium text-on-surface', alignClass(col.align), dense ? 'px-3 py-1.5' : 'px-4 py-3']\"\n >\n <!-- First column gets indent + expand icon -->\n <div v-if=\"ci === 0\" class=\"flex items-center gap-1\" :style=\"{ paddingLeft: `${item.depth * indent}px` }\">\n <button\n v-if=\"item.hasChildren\"\n type=\"button\"\n class=\"flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-transform duration-200 hover:bg-on-surface/8\"\n :class=\"item.isExpanded ? 'rotate-90' : ''\"\n @click.stop=\"toggleExpand(item.row)\"\n >\n <MIcon name=\"chevron_right\" :size=\"18\" />\n </button>\n <span v-else class=\"w-6 shrink-0\" />\n <slot :name=\"`cell-${col.key}`\" :row=\"item.row\" :value=\"item.row[col.key]\" :depth=\"item.depth\">\n <span :class=\"item.hasChildren ? 'font-medium' : ''\">{{ item.row[col.key] ?? '—' }}</span>\n </slot>\n </div>\n <template v-else>\n <slot :name=\"`cell-${col.key}`\" :row=\"item.row\" :value=\"item.row[col.key]\" :depth=\"item.depth\">\n {{ item.row[col.key] ?? '—' }}\n </slot>\n </template>\n </td>\n </tr>\n <tr v-if=\"!flatRows.length\">\n <td :colspan=\"columns.length\" class=\"border-t border-outline-variant px-4 py-10 text-center\">\n <MIcon name=\"account_tree\" :size=\"36\" class=\"mb-2 text-on-surface-variant opacity-30\" />\n <p class=\"text-body-medium text-on-surface-variant\">Sin datos</p>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface VTableColumn {\n key: string\n label: string\n width?: string\n align?: 'left' | 'center' | 'right'\n sortable?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n columns: VTableColumn[]\n rows: Record<string, any>[]\n rowHeight?: number\n rowKey?: string\n overscan?: number\n maxHeight?: string\n}>(), {\n rowHeight: 44,\n rowKey: 'id',\n overscan: 5,\n maxHeight: '500px',\n})\n\nconst emit = defineEmits<{ rowClick: [Record<string, any>] }>()\n\nconst scrollEl = ref<HTMLElement>()\nconst scrollTop = ref(0)\nconst containerH = ref(400)\n\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) { sortKey.value = key; sortDir.value = 'asc' }\n else if (sortDir.value === 'asc') sortDir.value = 'desc'\n else { sortKey.value = ''; sortDir.value = '' }\n}\n\nconst sortedRows = computed(() => {\n if (!sortKey.value || !sortDir.value) return props.rows\n const key = sortKey.value, dir = sortDir.value\n return [...props.rows].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, { numeric: true, sensitivity: 'base' })\n return dir === 'asc' ? cmp : -cmp\n })\n})\n\nconst totalHeight = computed(() => sortedRows.value.length * props.rowHeight)\n\nconst visibleRange = computed(() => {\n const start = Math.max(0, Math.floor(scrollTop.value / props.rowHeight) - props.overscan)\n const end = Math.min(\n sortedRows.value.length,\n Math.ceil((scrollTop.value + containerH.value) / props.rowHeight) + props.overscan\n )\n return { start, end }\n})\n\nconst visibleRows = computed(() =>\n sortedRows.value.slice(visibleRange.value.start, visibleRange.value.end).map((row, i) => ({\n row,\n index: visibleRange.value.start + i,\n top: (visibleRange.value.start + i) * props.rowHeight,\n }))\n)\n\nfunction onScroll() {\n if (!scrollEl.value) return\n scrollTop.value = scrollEl.value.scrollTop\n}\n\nlet ro: ResizeObserver | null = null\nonMounted(() => {\n if (scrollEl.value) {\n containerH.value = scrollEl.value.clientHeight\n ro = new ResizeObserver((entries) => { containerH.value = entries[0]!.contentRect.height })\n ro.observe(scrollEl.value)\n }\n})\nonBeforeUnmount(() => ro?.disconnect())\n\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex bg-surface-container-high\">\n <div\n v-for=\"col in columns\"\n :key=\"col.key\"\n :style=\"{ width: col.width || 'auto', flex: col.width ? 'none' : '1' }\"\n :class=\"[\n 'px-4 py-3 text-label-medium font-medium text-on-surface-variant whitespace-nowrap',\n alignClass(col.align),\n col.sortable ? 'cursor-pointer select-none hover:text-on-surface transition-colors' : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon v-if=\"sortKey === col.key && sortDir === 'asc'\" name=\"arrow_upward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else-if=\"sortKey === col.key && sortDir === 'desc'\" name=\"arrow_downward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n </div>\n </div>\n\n <!-- Virtual scroll body -->\n <div\n ref=\"scrollEl\"\n class=\"overflow-y-auto\"\n :style=\"{ maxHeight }\"\n @scroll=\"onScroll\"\n >\n <div class=\"relative\" :style=\"{ height: `${totalHeight}px` }\">\n <div\n v-for=\"{ row, index, top } in visibleRows\"\n :key=\"row[rowKey] ?? index\"\n class=\"absolute left-0 right-0 flex border-t border-outline-variant transition-colors duration-75 hover:bg-on-surface/[0.04]\"\n :class=\"index % 2 === 0 ? '' : 'bg-surface-container-lowest/50'\"\n :style=\"{ top: `${top}px`, height: `${rowHeight}px` }\"\n @click=\"emit('rowClick', row)\"\n >\n <div\n v-for=\"col in columns\"\n :key=\"col.key\"\n class=\"flex items-center overflow-hidden px-4 text-body-medium text-on-surface\"\n :style=\"{ width: col.width || 'auto', flex: col.width ? 'none' : '1' }\"\n :class=\"alignClass(col.align)\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\">\n <span class=\"truncate\">{{ row[col.key] ?? '—' }}</span>\n </slot>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ sortedRows.length.toLocaleString() }} filas\n <template v-if=\"visibleRange.end - visibleRange.start < sortedRows.length\">\n · mostrando {{ visibleRange.start + 1 }}–{{ visibleRange.end }}\n </template>\n </span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport MIcon from './MIcon.vue'\n\nexport interface VTableColumn {\n key: string\n label: string\n width?: string\n align?: 'left' | 'center' | 'right'\n sortable?: boolean\n}\n\nconst props = withDefaults(defineProps<{\n columns: VTableColumn[]\n rows: Record<string, any>[]\n rowHeight?: number\n rowKey?: string\n overscan?: number\n maxHeight?: string\n}>(), {\n rowHeight: 44,\n rowKey: 'id',\n overscan: 5,\n maxHeight: '500px',\n})\n\nconst emit = defineEmits<{ rowClick: [Record<string, any>] }>()\n\nconst scrollEl = ref<HTMLElement>()\nconst scrollTop = ref(0)\nconst containerH = ref(400)\n\nconst sortKey = ref('')\nconst sortDir = ref<'asc' | 'desc' | ''>('')\n\nfunction toggleSort(key: string) {\n if (sortKey.value !== key) { sortKey.value = key; sortDir.value = 'asc' }\n else if (sortDir.value === 'asc') sortDir.value = 'desc'\n else { sortKey.value = ''; sortDir.value = '' }\n}\n\nconst sortedRows = computed(() => {\n if (!sortKey.value || !sortDir.value) return props.rows\n const key = sortKey.value, dir = sortDir.value\n return [...props.rows].sort((a, b) => {\n const cmp = String(a[key] ?? '').localeCompare(String(b[key] ?? ''), undefined, { numeric: true, sensitivity: 'base' })\n return dir === 'asc' ? cmp : -cmp\n })\n})\n\nconst totalHeight = computed(() => sortedRows.value.length * props.rowHeight)\n\nconst visibleRange = computed(() => {\n const start = Math.max(0, Math.floor(scrollTop.value / props.rowHeight) - props.overscan)\n const end = Math.min(\n sortedRows.value.length,\n Math.ceil((scrollTop.value + containerH.value) / props.rowHeight) + props.overscan\n )\n return { start, end }\n})\n\nconst visibleRows = computed(() =>\n sortedRows.value.slice(visibleRange.value.start, visibleRange.value.end).map((row, i) => ({\n row,\n index: visibleRange.value.start + i,\n top: (visibleRange.value.start + i) * props.rowHeight,\n }))\n)\n\nfunction onScroll() {\n if (!scrollEl.value) return\n scrollTop.value = scrollEl.value.scrollTop\n}\n\nlet ro: ResizeObserver | null = null\nonMounted(() => {\n if (scrollEl.value) {\n containerH.value = scrollEl.value.clientHeight\n ro = new ResizeObserver((entries) => { containerH.value = entries[0]!.contentRect.height })\n ro.observe(scrollEl.value)\n }\n})\nonBeforeUnmount(() => ro?.disconnect())\n\nfunction alignClass(a?: string) { return a === 'center' ? 'text-center' : a === 'right' ? 'text-right' : 'text-left' }\n</script>\n\n<template>\n <div class=\"flex flex-col overflow-hidden rounded-sm border border-outline-variant\">\n <!-- Header -->\n <div class=\"flex bg-surface-container-high\">\n <div\n v-for=\"col in columns\"\n :key=\"col.key\"\n :style=\"{ width: col.width || 'auto', flex: col.width ? 'none' : '1' }\"\n :class=\"[\n 'px-4 py-3 text-label-medium font-medium text-on-surface-variant whitespace-nowrap',\n alignClass(col.align),\n col.sortable ? 'cursor-pointer select-none hover:text-on-surface transition-colors' : '',\n ]\"\n @click=\"col.sortable ? toggleSort(col.key) : undefined\"\n >\n <span class=\"inline-flex items-center gap-1\">\n {{ col.label }}\n <span v-if=\"col.sortable\" class=\"inline-flex\">\n <MIcon v-if=\"sortKey === col.key && sortDir === 'asc'\" name=\"arrow_upward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else-if=\"sortKey === col.key && sortDir === 'desc'\" name=\"arrow_downward\" :size=\"14\" class=\"text-primary\" />\n <MIcon v-else name=\"unfold_more\" :size=\"14\" class=\"opacity-30\" />\n </span>\n </span>\n </div>\n </div>\n\n <!-- Virtual scroll body -->\n <div\n ref=\"scrollEl\"\n class=\"overflow-y-auto\"\n :style=\"{ maxHeight }\"\n @scroll=\"onScroll\"\n >\n <div class=\"relative\" :style=\"{ height: `${totalHeight}px` }\">\n <div\n v-for=\"{ row, index, top } in visibleRows\"\n :key=\"row[rowKey] ?? index\"\n class=\"absolute left-0 right-0 flex border-t border-outline-variant transition-colors duration-75 hover:bg-on-surface/[0.04]\"\n :class=\"index % 2 === 0 ? '' : 'bg-surface-container-lowest/50'\"\n :style=\"{ top: `${top}px`, height: `${rowHeight}px` }\"\n @click=\"emit('rowClick', row)\"\n >\n <div\n v-for=\"col in columns\"\n :key=\"col.key\"\n class=\"flex items-center overflow-hidden px-4 text-body-medium text-on-surface\"\n :style=\"{ width: col.width || 'auto', flex: col.width ? 'none' : '1' }\"\n :class=\"alignClass(col.align)\"\n >\n <slot :name=\"`cell-${col.key}`\" :row=\"row\" :value=\"row[col.key]\">\n <span class=\"truncate\">{{ row[col.key] ?? '—' }}</span>\n </slot>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"border-t border-outline-variant bg-surface-container-lowest px-4 py-2\">\n <span class=\"text-label-small text-on-surface-variant\">\n {{ sortedRows.length.toLocaleString() }} filas\n <template v-if=\"visibleRange.end - visibleRange.start < sortedRows.length\">\n · mostrando {{ visibleRange.start + 1 }}–{{ visibleRange.end }}\n </template>\n </span>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|