@m3ui-vue/m3ui-vue 0.5.1 → 0.5.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MMenuItem-DyY4zhRA.js","names":["$slots","$slots"],"sources":["../src/components/MSpinner.vue","../src/components/MSpinner.vue","../src/composables/useFieldBg.ts","../src/composables/useDebounce.ts","../src/components/MIconButton.vue","../src/components/MIconButton.vue","../src/components/MButton.vue","../src/components/MButton.vue","../src/components/MDialog.vue","../src/components/MDialog.vue","../src/components/MTextField.vue","../src/components/MTextField.vue","../src/components/MMenu.vue","../src/components/MMenu.vue","../src/components/MMenuItem.vue","../src/components/MMenuItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useLocale } from '../composables/useLocale';\n\nconst props = withDefaults(\n defineProps<{\n size?: number;\n wavy?: boolean;\n label?: string;\n }>(),\n { size: 20, wavy: false },\n);\n\nconst locale = useLocale();\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=\"label ?? locale.loading\"\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 { useLocale } from '../composables/useLocale';\n\nconst props = withDefaults(\n defineProps<{\n size?: number;\n wavy?: boolean;\n label?: string;\n }>(),\n { size: 20, wavy: false },\n);\n\nconst locale = useLocale();\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=\"label ?? locale.loading\"\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","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","import { onBeforeUnmount } from 'vue'\n\nexport function useDebounce(fn: (...args: any[]) => void, ms: number) {\n let timer: ReturnType<typeof setTimeout> | null = null\n\n function debounced(...args: any[]) {\n if (timer) clearTimeout(timer)\n timer = setTimeout(() => {\n timer = null\n fn(...args)\n }, ms)\n }\n\n function cancel() {\n if (timer) {\n clearTimeout(timer)\n timer = null\n }\n }\n\n onBeforeUnmount(cancel)\n\n return { debounced, cancel }\n}\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 shape?: 'rounded' | 'squared'\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number\n disabled?: boolean\n to?: string | Record<string, any>\n }>(),\n {\n variant: 'standard',\n shape: 'rounded',\n size: 'sm',\n disabled: false,\n },\n)\n\nconst tag = computed(() => props.to ? 'RouterLink' : 'button')\n\nconst sizeMap = {\n xs: { px: 32, icon: 16 },\n sm: { px: 40, icon: 20 },\n md: { px: 48, icon: 24 },\n lg: { px: 56, icon: 28 },\n xl: { px: 64, icon: 32 },\n}\n\nconst resolved = computed(() => {\n if (typeof props.size === 'number') return { px: props.size, icon: Math.round(props.size * 0.55) }\n return sizeMap[props.size] ?? sizeMap.sm\n})\n\nconst base =\n 'inline-flex shrink-0 items-center justify-center transition-colors duration-150 cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38]'\n\nconst shapeClass = computed(() => props.shape === 'squared' ? 'rounded-md' : 'rounded-full')\n\nfunction createRipple(event: PointerEvent) {\n if (props.disabled) 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\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 <component\n :is=\"tag\"\n :to=\"to || undefined\"\n :type=\"to ? undefined : 'button'\"\n :aria-label=\"label || undefined\"\n :disabled=\"disabled\"\n :class=\"[base, shapeClass, variantClasses, 'relative overflow-hidden']\"\n :style=\"{ width: `${resolved.px}px`, height: `${resolved.px}px` }\"\n @pointerdown=\"createRipple\"\n >\n <MIcon :name=\"icon\" :size=\"resolved.icon\" />\n </component>\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 shape?: 'rounded' | 'squared'\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number\n disabled?: boolean\n to?: string | Record<string, any>\n }>(),\n {\n variant: 'standard',\n shape: 'rounded',\n size: 'sm',\n disabled: false,\n },\n)\n\nconst tag = computed(() => props.to ? 'RouterLink' : 'button')\n\nconst sizeMap = {\n xs: { px: 32, icon: 16 },\n sm: { px: 40, icon: 20 },\n md: { px: 48, icon: 24 },\n lg: { px: 56, icon: 28 },\n xl: { px: 64, icon: 32 },\n}\n\nconst resolved = computed(() => {\n if (typeof props.size === 'number') return { px: props.size, icon: Math.round(props.size * 0.55) }\n return sizeMap[props.size] ?? sizeMap.sm\n})\n\nconst base =\n 'inline-flex shrink-0 items-center justify-center transition-colors duration-150 cursor-pointer ' +\n 'disabled:cursor-not-allowed disabled:opacity-[0.38]'\n\nconst shapeClass = computed(() => props.shape === 'squared' ? 'rounded-md' : 'rounded-full')\n\nfunction createRipple(event: PointerEvent) {\n if (props.disabled) 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\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 <component\n :is=\"tag\"\n :to=\"to || undefined\"\n :type=\"to ? undefined : 'button'\"\n :aria-label=\"label || undefined\"\n :disabled=\"disabled\"\n :class=\"[base, shapeClass, variantClasses, 'relative overflow-hidden']\"\n :style=\"{ width: `${resolved.px}px`, height: `${resolved.px}px` }\"\n @pointerdown=\"createRipple\"\n >\n <MIcon :name=\"icon\" :size=\"resolved.icon\" />\n </component>\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 size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n shape?: 'rounded' | 'squared'\n to?: string | Record<string, any>\n }>(),\n {\n variant: 'filled',\n color: 'primary',\n type: 'button',\n disabled: false,\n loading: false,\n size: 'sm',\n shape: 'rounded',\n },\n)\n\nconst tag = computed(() => props.to ? 'RouterLink' : 'button')\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\nconst base =\n 'relative inline-flex items-center justify-center gap-2 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 shapeClass = computed(() => props.shape === 'squared' ? 'rounded-md' : 'rounded-full')\n\nconst sizeMap = {\n xs: { h: 'h-8', text: 'text-label-medium', icon: 16, spinner: 14, px: 'px-4', pxIcon: 'pl-3 pr-4', pxText: 'px-2' },\n sm: { h: 'h-10', text: 'text-label-large', icon: 20, spinner: 16, px: 'px-6', pxIcon: 'pl-4 pr-6', pxText: 'px-3' },\n md: { h: 'h-14', text: 'text-title-medium', icon: 20, spinner: 18, px: 'px-6', pxIcon: 'pl-5 pr-6', pxText: 'px-3' },\n lg: { h: 'h-16', text: 'text-title-large', icon: 22, spinner: 20, px: 'px-7', pxIcon: 'pl-6 pr-7', pxText: 'px-4' },\n xl: { h: 'h-20', text: 'text-headline-small', icon: 24, spinner: 22, px: 'px-8', pxIcon: 'pl-7 pr-8', pxText: 'px-4' },\n}\n\nconst s = computed(() => sizeMap[props.size] ?? sizeMap.sm)\nconst sizeClasses = computed(() => `${s.value.h} ${s.value.text}`)\nconst iconSize = computed(() => s.value.icon)\nconst spinnerSize = computed(() => s.value.spinner)\n\nconst px = computed(() => {\n if (props.variant === 'text') return s.value.pxText\n return (props.icon || props.loading) ? s.value.pxIcon : s.value.px\n})\n\nconst variantClasses = computed(() => {\n const err = isError.value\n switch (props.variant) {\n case 'filled':\n return err\n ? 'bg-error text-on-error enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'bg-primary text-on-primary enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'tonal':\n return err\n ? 'bg-error-container text-on-error-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'bg-secondary-container text-on-secondary-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'elevated':\n return err\n ? 'bg-surface-container-low text-error shadow-elevation-1 enabled:hover:shadow-elevation-2'\n : 'bg-surface-container-low text-primary shadow-elevation-1 enabled:hover:shadow-elevation-2'\n case 'outlined':\n return err\n ? 'border border-error text-error'\n : 'border border-outline text-primary'\n case 'text':\n return err\n ? 'text-error'\n : '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 <component\n :is=\"tag\"\n :to=\"to || undefined\"\n :type=\"to ? undefined : type\"\n :disabled=\"disabled || loading\"\n :class=\"[base, shapeClass, sizeClasses, px, variantClasses]\"\n :style=\"customStyle\"\n @pointerdown=\"createRipple\"\n >\n <MSpinner v-if=\"loading\" :size=\"spinnerSize\" />\n <MIcon v-else-if=\"icon\" :name=\"icon\" :size=\"iconSize\" />\n <slot />\n </component>\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 size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n shape?: 'rounded' | 'squared'\n to?: string | Record<string, any>\n }>(),\n {\n variant: 'filled',\n color: 'primary',\n type: 'button',\n disabled: false,\n loading: false,\n size: 'sm',\n shape: 'rounded',\n },\n)\n\nconst tag = computed(() => props.to ? 'RouterLink' : 'button')\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\nconst base =\n 'relative inline-flex items-center justify-center gap-2 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 shapeClass = computed(() => props.shape === 'squared' ? 'rounded-md' : 'rounded-full')\n\nconst sizeMap = {\n xs: { h: 'h-8', text: 'text-label-medium', icon: 16, spinner: 14, px: 'px-4', pxIcon: 'pl-3 pr-4', pxText: 'px-2' },\n sm: { h: 'h-10', text: 'text-label-large', icon: 20, spinner: 16, px: 'px-6', pxIcon: 'pl-4 pr-6', pxText: 'px-3' },\n md: { h: 'h-14', text: 'text-title-medium', icon: 20, spinner: 18, px: 'px-6', pxIcon: 'pl-5 pr-6', pxText: 'px-3' },\n lg: { h: 'h-16', text: 'text-title-large', icon: 22, spinner: 20, px: 'px-7', pxIcon: 'pl-6 pr-7', pxText: 'px-4' },\n xl: { h: 'h-20', text: 'text-headline-small', icon: 24, spinner: 22, px: 'px-8', pxIcon: 'pl-7 pr-8', pxText: 'px-4' },\n}\n\nconst s = computed(() => sizeMap[props.size] ?? sizeMap.sm)\nconst sizeClasses = computed(() => `${s.value.h} ${s.value.text}`)\nconst iconSize = computed(() => s.value.icon)\nconst spinnerSize = computed(() => s.value.spinner)\n\nconst px = computed(() => {\n if (props.variant === 'text') return s.value.pxText\n return (props.icon || props.loading) ? s.value.pxIcon : s.value.px\n})\n\nconst variantClasses = computed(() => {\n const err = isError.value\n switch (props.variant) {\n case 'filled':\n return err\n ? 'bg-error text-on-error enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'bg-primary text-on-primary enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'tonal':\n return err\n ? 'bg-error-container text-on-error-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n : 'bg-secondary-container text-on-secondary-container enabled:hover:shadow-elevation-1 enabled:active:shadow-none'\n case 'elevated':\n return err\n ? 'bg-surface-container-low text-error shadow-elevation-1 enabled:hover:shadow-elevation-2'\n : 'bg-surface-container-low text-primary shadow-elevation-1 enabled:hover:shadow-elevation-2'\n case 'outlined':\n return err\n ? 'border border-error text-error'\n : 'border border-outline text-primary'\n case 'text':\n return err\n ? 'text-error'\n : '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 <component\n :is=\"tag\"\n :to=\"to || undefined\"\n :type=\"to ? undefined : type\"\n :disabled=\"disabled || loading\"\n :class=\"[base, shapeClass, sizeClasses, px, variantClasses]\"\n :style=\"customStyle\"\n @pointerdown=\"createRipple\"\n >\n <MSpinner v-if=\"loading\" :size=\"spinnerSize\" />\n <MIcon v-else-if=\"icon\" :name=\"icon\" :size=\"iconSize\" />\n <slot />\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIconButton from './MIconButton.vue'\nimport { useLocale } from '../composables/useLocale'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n title?: string\n maxWidth?: string\n persistent?: boolean\n fullscreen?: boolean\n closeLabel?: string\n }>(),\n {\n maxWidth: 'max-w-md',\n persistent: false,\n fullscreen: false,\n },\n)\n\nconst locale = useLocale()\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 <!-- Basic dialog -->\n <Transition v-if=\"!fullscreen\" 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=\"closeLabel ?? locale.close\" @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\n <!-- Fullscreen dialog -->\n <Transition v-else name=\"m3-dialog-fs\">\n <div\n v-if=\"modelValue\"\n class=\"dialog-fs fixed inset-0 z-50 flex flex-col bg-surface\"\n >\n <div class=\"flex h-14 shrink-0 items-center gap-2 px-2\">\n <MIconButton v-if=\"!persistent\" icon=\"close\" :label=\"closeLabel ?? locale.close\" @click=\"close\" />\n <h2 class=\"flex-1 text-title-large font-medium text-on-surface\">\n <slot name=\"title\">{{ title }}</slot>\n </h2>\n <div v-if=\"$slots.actions\" class=\"flex items-center gap-2\">\n <slot name=\"actions\" />\n </div>\n </div>\n <div class=\"flex-1 overflow-y-auto px-6 py-4 text-body-medium text-on-surface-variant\">\n <slot />\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\n.m3-dialog-fs-enter-active,\n.m3-dialog-fs-leave-active {\n transition: transform 0.25s cubic-bezier(0.2, 0, 0, 1), opacity 0.15s ease;\n}\n.m3-dialog-fs-enter-from {\n transform: translateY(100%);\n opacity: 0;\n}\n.m3-dialog-fs-leave-to {\n transform: translateY(100%);\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { watch } from 'vue'\nimport MIconButton from './MIconButton.vue'\nimport { useLocale } from '../composables/useLocale'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n title?: string\n maxWidth?: string\n persistent?: boolean\n fullscreen?: boolean\n closeLabel?: string\n }>(),\n {\n maxWidth: 'max-w-md',\n persistent: false,\n fullscreen: false,\n },\n)\n\nconst locale = useLocale()\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 <!-- Basic dialog -->\n <Transition v-if=\"!fullscreen\" 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=\"closeLabel ?? locale.close\" @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\n <!-- Fullscreen dialog -->\n <Transition v-else name=\"m3-dialog-fs\">\n <div\n v-if=\"modelValue\"\n class=\"dialog-fs fixed inset-0 z-50 flex flex-col bg-surface\"\n >\n <div class=\"flex h-14 shrink-0 items-center gap-2 px-2\">\n <MIconButton v-if=\"!persistent\" icon=\"close\" :label=\"closeLabel ?? locale.close\" @click=\"close\" />\n <h2 class=\"flex-1 text-title-large font-medium text-on-surface\">\n <slot name=\"title\">{{ title }}</slot>\n </h2>\n <div v-if=\"$slots.actions\" class=\"flex items-center gap-2\">\n <slot name=\"actions\" />\n </div>\n </div>\n <div class=\"flex-1 overflow-y-auto px-6 py-4 text-body-medium text-on-surface-variant\">\n <slot />\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\n.m3-dialog-fs-enter-active,\n.m3-dialog-fs-leave-active {\n transition: transform 0.25s cubic-bezier(0.2, 0, 0, 1), opacity 0.15s ease;\n}\n.m3-dialog-fs-enter-from {\n transform: translateY(100%);\n opacity: 0;\n}\n.m3-dialog-fs-leave-to {\n transform: translateY(100%);\n opacity: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useId, useSlots } from \"vue\";\nimport MIcon from \"./MIcon.vue\";\nimport { useFieldBg } from \"../composables/useFieldBg\";\nimport { useDebounce } from \"../composables/useDebounce\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number;\n label: string;\n type?: string;\n variant?: \"filled\" | \"outlined\";\n error?: boolean;\n errorLabel?: string;\n hint?: string;\n disabled?: boolean;\n required?: boolean;\n multiline?: boolean;\n rows?: number;\n autocomplete?: string;\n leadingIcon?: string;\n clearable?: boolean;\n fieldBg?: string;\n debounce?: number;\n }>(),\n {\n type: \"text\",\n variant: \"filled\",\n rows: 3,\n clearable: false,\n debounce: 0,\n },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [string]; debounced: [string] }>();\n\nconst id = useId();\nconst slots = useSlots();\n\nconst fieldBgEl = ref<HTMLElement | null>(null);\nconst { resolvedFieldBg } = useFieldBg(fieldBgEl, () => props.fieldBg);\n\nconst { debounced: emitDebounced, cancel: cancelDebounce } = useDebounce((v: string) => emit(\"debounced\", v), props.debounce)\n\nconst showClear = computed(() => props.clearable && String(props.modelValue).length > 0 && !props.disabled)\n\nconst inputClasses = computed(() => {\n const hasTrailing = !!slots.trailing || props.clearable;\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 unfloatedTop = props.variant === 'filled' ? 'top-[53%]' : 'top-1/2'\n const base = [\n \"pointer-events-none absolute truncate transition-all duration-200\",\n left,\n \"right-4\",\n `${unfloatedTop} -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 if (props.debounce > 0) emitDebounced(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 <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 text-on-surface-variant\"\n :class=\"multiline ? 'top-[55%] -translate-y-1/2' : variant === 'filled' ? 'top-5' : 'top-4.5'\"\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\">&nbsp;*</span>\n </label>\n\n <div v-if=\"$slots.trailing\" class=\"absolute right-2 top-1/2 -translate-y-1/2\">\n <slot name=\"trailing\" />\n </div>\n <button\n v-else-if=\"showClear\"\n type=\"button\"\n class=\"absolute right-3 top-1/2 -translate-y-1/2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8 hover:text-on-surface\"\n @click=\"cancelDebounce(); emit('update:modelValue', '')\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <p v-if=\"error && errorLabel\" class=\"px-4 text-body-small text-error\">{{ errorLabel }}</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\";\nimport { useDebounce } from \"../composables/useDebounce\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string | number;\n label: string;\n type?: string;\n variant?: \"filled\" | \"outlined\";\n error?: boolean;\n errorLabel?: string;\n hint?: string;\n disabled?: boolean;\n required?: boolean;\n multiline?: boolean;\n rows?: number;\n autocomplete?: string;\n leadingIcon?: string;\n clearable?: boolean;\n fieldBg?: string;\n debounce?: number;\n }>(),\n {\n type: \"text\",\n variant: \"filled\",\n rows: 3,\n clearable: false,\n debounce: 0,\n },\n);\n\nconst emit = defineEmits<{ \"update:modelValue\": [string]; debounced: [string] }>();\n\nconst id = useId();\nconst slots = useSlots();\n\nconst fieldBgEl = ref<HTMLElement | null>(null);\nconst { resolvedFieldBg } = useFieldBg(fieldBgEl, () => props.fieldBg);\n\nconst { debounced: emitDebounced, cancel: cancelDebounce } = useDebounce((v: string) => emit(\"debounced\", v), props.debounce)\n\nconst showClear = computed(() => props.clearable && String(props.modelValue).length > 0 && !props.disabled)\n\nconst inputClasses = computed(() => {\n const hasTrailing = !!slots.trailing || props.clearable;\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 unfloatedTop = props.variant === 'filled' ? 'top-[53%]' : 'top-1/2'\n const base = [\n \"pointer-events-none absolute truncate transition-all duration-200\",\n left,\n \"right-4\",\n `${unfloatedTop} -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 if (props.debounce > 0) emitDebounced(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 <div\n v-if=\"leadingIcon\"\n class=\"pointer-events-none absolute left-3.5 text-on-surface-variant\"\n :class=\"multiline ? 'top-[55%] -translate-y-1/2' : variant === 'filled' ? 'top-5' : 'top-4.5'\"\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\">&nbsp;*</span>\n </label>\n\n <div v-if=\"$slots.trailing\" class=\"absolute right-2 top-1/2 -translate-y-1/2\">\n <slot name=\"trailing\" />\n </div>\n <button\n v-else-if=\"showClear\"\n type=\"button\"\n class=\"absolute right-3 top-1/2 -translate-y-1/2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-on-surface-variant transition-colors hover:bg-on-surface/8 hover:text-on-surface\"\n @click=\"cancelDebounce(); emit('update:modelValue', '')\"\n >\n <MIcon name=\"close\" :size=\"18\" />\n </button>\n </div>\n\n <p v-if=\"error && errorLabel\" class=\"px-4 text-body-small text-error\">{{ errorLabel }}</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, onMounted, onUnmounted, provide, 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\nprovide('m-menu-close', close)\ndefineExpose({ close, open })\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (dropdownEl.value?.contains(t)) return\n if ((t as Element).closest?.('.m3-submenu')) return\n 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-hidden rounded-lg bg-surface-container shadow-elevation-2\"\n :style=\"{ ...dropStyle, transformOrigin: origin }\"\n >\n <div class=\"overflow-y-auto py-1\" :style=\"{ maxHeight: dropStyle.maxHeight }\">\n <slot :close=\"close\" />\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, provide, 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\nprovide('m-menu-close', close)\ndefineExpose({ close, open })\n\nfunction onOutsideClick(e: MouseEvent) {\n const t = e.target as Node\n if (triggerEl.value?.contains(t)) return\n if (dropdownEl.value?.contains(t)) return\n if ((t as Element).closest?.('.m3-submenu')) return\n 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-hidden rounded-lg bg-surface-container shadow-elevation-2\"\n :style=\"{ ...dropStyle, transformOrigin: origin }\"\n >\n <div class=\"overflow-y-auto py-1\" :style=\"{ maxHeight: dropStyle.maxHeight }\">\n <slot :close=\"close\" />\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref, useSlots } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n icon?: string\n shortcut?: string\n to?: string | Record<string, any>\n disabled?: boolean\n danger?: boolean\n}>(), {\n disabled: false,\n danger: false,\n})\n\nconst emit = defineEmits<{ click: [] }>()\n\nconst slots = useSlots()\nconst tag = computed(() => props.to ? 'RouterLink' : 'button')\nconst hasChildren = computed(() => !!slots.children)\nconst menuClose = inject<(() => void) | null>('m-menu-close', null)\n\nfunction handleClick() {\n if (props.disabled || hasChildren.value) return\n emit('click')\n menuClose?.()\n}\nconst showSub = ref(false)\nconst itemEl = ref<HTMLElement>()\nconst subStyle = ref<Record<string, string>>({})\n\nfunction onMouseEnter() {\n if (!hasChildren.value || props.disabled) return\n showSub.value = true\n if (!itemEl.value) return\n const rect = itemEl.value.getBoundingClientRect()\n const right = rect.right + 200 > window.innerWidth\n subStyle.value = {\n position: 'fixed',\n top: `${rect.top}px`,\n ...(right\n ? { right: `${window.innerWidth - rect.left}px` }\n : { left: `${rect.right}px` }),\n zIndex: '501',\n }\n}\n\nfunction onMouseLeave(e: MouseEvent) {\n const related = e.relatedTarget as Element | null\n if (related?.closest('.m3-submenu')) return\n showSub.value = false\n}\n\nfunction onSubLeave(e: MouseEvent) {\n const related = e.relatedTarget as Element | null\n if (related?.closest('.m3-submenu') || itemEl.value?.contains(related as Node)) return\n showSub.value = false\n}\n</script>\n\n<template>\n <div\n ref=\"itemEl\"\n @mouseenter=\"onMouseEnter\"\n @mouseleave=\"onMouseLeave\"\n >\n <component\n :is=\"tag\"\n :to=\"to || undefined\"\n :type=\"to ? undefined : 'button'\"\n class=\"flex w-full items-center gap-3 px-4 py-2.5 text-left text-body-large transition-colors\"\n :class=\"[\n disabled ? 'cursor-not-allowed opacity-[0.38] text-on-surface' : 'cursor-pointer',\n !disabled && danger ? 'text-error hover:bg-error/8' : '',\n !disabled && !danger ? 'text-on-surface hover:bg-on-surface/8' : '',\n ]\"\n :disabled=\"disabled || undefined\"\n @click=\"handleClick\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"20\" class=\"shrink-0\" :class=\"danger ? 'text-error' : 'text-on-surface-variant'\" />\n <span class=\"flex-1\"><slot /></span>\n <span v-if=\"shortcut && !hasChildren\" class=\"ml-4 shrink-0 text-label-small text-on-surface-variant opacity-60\">{{ shortcut }}</span>\n <MIcon v-if=\"hasChildren\" name=\"chevron_right\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n </component>\n\n <!-- Submenu -->\n <Teleport v-if=\"hasChildren\" to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-100\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"showSub\"\n class=\"m3-submenu min-w-44 overflow-hidden rounded-lg bg-surface-container py-1 shadow-elevation-2\"\n :style=\"subStyle\"\n @mouseleave=\"onSubLeave\"\n >\n <slot name=\"children\" />\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref, useSlots } from 'vue'\nimport MIcon from './MIcon.vue'\n\nconst props = withDefaults(defineProps<{\n icon?: string\n shortcut?: string\n to?: string | Record<string, any>\n disabled?: boolean\n danger?: boolean\n}>(), {\n disabled: false,\n danger: false,\n})\n\nconst emit = defineEmits<{ click: [] }>()\n\nconst slots = useSlots()\nconst tag = computed(() => props.to ? 'RouterLink' : 'button')\nconst hasChildren = computed(() => !!slots.children)\nconst menuClose = inject<(() => void) | null>('m-menu-close', null)\n\nfunction handleClick() {\n if (props.disabled || hasChildren.value) return\n emit('click')\n menuClose?.()\n}\nconst showSub = ref(false)\nconst itemEl = ref<HTMLElement>()\nconst subStyle = ref<Record<string, string>>({})\n\nfunction onMouseEnter() {\n if (!hasChildren.value || props.disabled) return\n showSub.value = true\n if (!itemEl.value) return\n const rect = itemEl.value.getBoundingClientRect()\n const right = rect.right + 200 > window.innerWidth\n subStyle.value = {\n position: 'fixed',\n top: `${rect.top}px`,\n ...(right\n ? { right: `${window.innerWidth - rect.left}px` }\n : { left: `${rect.right}px` }),\n zIndex: '501',\n }\n}\n\nfunction onMouseLeave(e: MouseEvent) {\n const related = e.relatedTarget as Element | null\n if (related?.closest('.m3-submenu')) return\n showSub.value = false\n}\n\nfunction onSubLeave(e: MouseEvent) {\n const related = e.relatedTarget as Element | null\n if (related?.closest('.m3-submenu') || itemEl.value?.contains(related as Node)) return\n showSub.value = false\n}\n</script>\n\n<template>\n <div\n ref=\"itemEl\"\n @mouseenter=\"onMouseEnter\"\n @mouseleave=\"onMouseLeave\"\n >\n <component\n :is=\"tag\"\n :to=\"to || undefined\"\n :type=\"to ? undefined : 'button'\"\n class=\"flex w-full items-center gap-3 px-4 py-2.5 text-left text-body-large transition-colors\"\n :class=\"[\n disabled ? 'cursor-not-allowed opacity-[0.38] text-on-surface' : 'cursor-pointer',\n !disabled && danger ? 'text-error hover:bg-error/8' : '',\n !disabled && !danger ? 'text-on-surface hover:bg-on-surface/8' : '',\n ]\"\n :disabled=\"disabled || undefined\"\n @click=\"handleClick\"\n >\n <MIcon v-if=\"icon\" :name=\"icon\" :size=\"20\" class=\"shrink-0\" :class=\"danger ? 'text-error' : 'text-on-surface-variant'\" />\n <span class=\"flex-1\"><slot /></span>\n <span v-if=\"shortcut && !hasChildren\" class=\"ml-4 shrink-0 text-label-small text-on-surface-variant opacity-60\">{{ shortcut }}</span>\n <MIcon v-if=\"hasChildren\" name=\"chevron_right\" :size=\"18\" class=\"shrink-0 text-on-surface-variant\" />\n </component>\n\n <!-- Submenu -->\n <Teleport v-if=\"hasChildren\" to=\"body\">\n <Transition\n enter-active-class=\"transition-opacity duration-100\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-75\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"showSub\"\n class=\"m3-submenu min-w-44 overflow-hidden rounded-lg bg-surface-container py-1 shadow-elevation-2\"\n :style=\"subStyle\"\n @mouseleave=\"onSubLeave\"\n >\n <slot name=\"children\" />\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
- import { t as e } from "./MIcon-CaEooCmZ.js";
2
- import { t } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
- import { computed as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createTextVNode as o, createVNode as s, defineComponent as c, normalizeClass as l, normalizeStyle as u, onBeforeUnmount as d, onMounted as f, openBlock as p, ref as m, renderSlot as h, toDisplayString as g, watch as _, withCtx as v } from "vue";
1
+ import { r as e } from "./useLocale-QCIVDYWx.js";
2
+ import { t } from "./MIcon-CaEooCmZ.js";
3
+ import { t as n } from "./_plugin-vue_export-helper-B3ysoDQm.js";
4
+ import { computed as r, createCommentVNode as i, createElementBlock as a, createElementVNode as o, createTextVNode as s, createVNode as c, defineComponent as l, normalizeClass as u, normalizeStyle as d, onBeforeUnmount as f, onMounted as p, openBlock as m, ref as h, renderSlot as g, toDisplayString as _, watch as v, withCtx as y } from "vue";
4
5
  //#region src/components/MCodeEditor.vue?vue&type=script&setup=true&lang.ts
5
- var y = { class: "overflow-hidden rounded-lg border border-outline-variant" }, b = { class: "flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-2" }, x = { class: "text-label-medium text-on-surface-variant" }, S = { class: "flex items-center gap-2" }, C = ["title"], w = {
6
+ var b = { class: "overflow-hidden rounded-lg border border-outline-variant" }, x = { class: "flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-2" }, S = { class: "text-label-medium text-on-surface-variant" }, C = { class: "flex items-center gap-2" }, w = ["title"], T = {
6
7
  key: 0,
7
8
  class: "text-primary"
8
- }, T = /*#__PURE__*/ t(/* @__PURE__ */ c({
9
+ }, E = /*#__PURE__*/ n(/* @__PURE__ */ l({
9
10
  __name: "MCodeEditor",
10
11
  props: {
11
12
  modelValue: {},
@@ -28,14 +29,14 @@ var y = { class: "overflow-hidden rounded-lg border border-outline-variant" }, b
28
29
  }
29
30
  },
30
31
  emits: ["update:modelValue"],
31
- setup(t, { emit: o }) {
32
- let c = t, v = o, T = m(null), E = null, D = null, O = m(!1);
32
+ setup(e, { emit: n }) {
33
+ let s = e, l = n, y = h(null), E = null, D = null, O = h(!1);
33
34
  async function k() {
34
- await navigator.clipboard.writeText(c.modelValue), O.value = !0, setTimeout(() => {
35
+ await navigator.clipboard.writeText(s.modelValue), O.value = !0, setTimeout(() => {
35
36
  O.value = !1;
36
37
  }, 1500);
37
38
  }
38
- let A = n(() => ({
39
+ let A = r(() => ({
39
40
  javascript: "JavaScript",
40
41
  typescript: "TypeScript",
41
42
  json: "JSON",
@@ -44,7 +45,7 @@ var y = { class: "overflow-hidden rounded-lg border border-outline-variant" }, b
44
45
  python: "Python",
45
46
  vue: "Vue",
46
47
  plain: "Texto"
47
- })[c.language]);
48
+ })[s.language]);
48
49
  async function j() {
49
50
  if (D) return D;
50
51
  let [e, t, n, r, i, a, o, s, c, l, u, d] = await Promise.all([
@@ -212,7 +213,7 @@ var y = { class: "overflow-hidden rounded-lg border border-outline-variant" }, b
212
213
  ]);
213
214
  }
214
215
  function N(e) {
215
- switch (c.language) {
216
+ switch (s.language) {
216
217
  case "javascript": return e.jsMod.javascript();
217
218
  case "typescript": return e.jsMod.javascript({ typescript: !0 });
218
219
  case "json": return e.jsonMod.json();
@@ -224,7 +225,7 @@ var y = { class: "overflow-hidden rounded-lg border border-outline-variant" }, b
224
225
  }
225
226
  }
226
227
  function P(e) {
227
- let { viewMod: t, stateMod: n, commandsMod: r, languageMod: i, highlightMod: a, oneDarkMod: o } = e, s = M(i, a.tags), l = [
228
+ let { viewMod: t, stateMod: n, commandsMod: r, languageMod: i, highlightMod: a, oneDarkMod: o } = e, c = M(i, a.tags), u = [
228
229
  t.keymap.of([
229
230
  ...r.defaultKeymap,
230
231
  ...r.historyKeymap,
@@ -234,64 +235,65 @@ var y = { class: "overflow-hidden rounded-lg border border-outline-variant" }, b
234
235
  i.bracketMatching(),
235
236
  i.indentOnInput(),
236
237
  i.foldGutter(),
238
+ t.drawSelection(),
237
239
  t.highlightActiveLine(),
238
240
  t.highlightActiveLineGutter(),
239
- i.syntaxHighlighting(s),
241
+ i.syntaxHighlighting(c),
240
242
  i.syntaxHighlighting(i.defaultHighlightStyle, { fallback: !0 }),
241
243
  N(e),
242
244
  t.EditorView.updateListener.of((e) => {
243
- e.docChanged && v("update:modelValue", e.state.doc.toString());
245
+ e.docChanged && l("update:modelValue", e.state.doc.toString());
244
246
  }),
245
- n.EditorState.readOnly.of(c.readonly)
247
+ n.EditorState.readOnly.of(s.readonly)
246
248
  ];
247
- return c.wrap && l.push(t.EditorView.lineWrapping), c.lineNumbers && l.push(t.lineNumbers()), c.theme === "dark" && l.push(o.oneDark), l;
249
+ return s.wrap && u.push(t.EditorView.lineWrapping), s.lineNumbers && u.push(t.lineNumbers()), s.theme === "dark" && u.push(o.oneDark), u;
248
250
  }
249
251
  async function F() {
250
- if (!T.value) return;
252
+ if (!y.value) return;
251
253
  let e = await j(), { viewMod: t, stateMod: n } = e;
252
254
  E?.destroy(), E = new t.EditorView({
253
255
  state: n.EditorState.create({
254
- doc: c.modelValue,
256
+ doc: s.modelValue,
255
257
  extensions: P(e)
256
258
  }),
257
- parent: T.value
259
+ parent: y.value
258
260
  });
259
261
  }
260
- return f(F), _(() => c.modelValue, (e) => {
262
+ return p(F), v(() => s.modelValue, (e) => {
261
263
  E && E.state.doc.toString() !== e && E.dispatch({ changes: {
262
264
  from: 0,
263
265
  to: E.state.doc.length,
264
266
  insert: e
265
267
  } });
266
- }), _([
267
- () => c.language,
268
- () => c.theme,
269
- () => c.readonly,
270
- () => c.lineNumbers,
271
- () => c.wrap
272
- ], F), d(() => E?.destroy()), (n, o) => (p(), i("div", y, [a("div", b, [a("span", x, g(A.value), 1), a("div", S, [h(n.$slots, "actions", {}, void 0, !0), a("button", {
268
+ }), v([
269
+ () => s.language,
270
+ () => s.theme,
271
+ () => s.readonly,
272
+ () => s.lineNumbers,
273
+ () => s.wrap
274
+ ], F), f(() => E?.destroy()), (n, r) => (m(), a("div", b, [o("div", x, [o("span", S, _(A.value), 1), o("div", C, [g(n.$slots, "actions", {}, void 0, !0), o("button", {
273
275
  type: "button",
274
276
  class: "flex h-7 cursor-pointer items-center gap-1.5 rounded-md px-2 text-label-medium text-on-surface-variant transition-colors hover:bg-on-surface/8",
275
277
  title: O.value ? "Copied!" : "Copy code",
276
278
  onClick: k
277
- }, [s(e, {
279
+ }, [c(t, {
278
280
  name: O.value ? "check" : "content_copy",
279
281
  size: 16,
280
- class: l(O.value ? "text-primary" : "")
281
- }, null, 8, ["name", "class"]), O.value ? (p(), i("span", w, "Copied")) : r("", !0)], 8, C)])]), a("div", {
282
+ class: u(O.value ? "text-primary" : "")
283
+ }, null, 8, ["name", "class"]), O.value ? (m(), a("span", T, "Copied")) : i("", !0)], 8, w)])]), o("div", {
282
284
  ref_key: "containerRef",
283
- ref: T,
285
+ ref: y,
284
286
  class: "code-editor-container overflow-auto bg-surface text-on-surface",
285
- style: u({
286
- minHeight: t.minHeight,
287
- maxHeight: t.maxHeight
287
+ style: d({
288
+ minHeight: e.minHeight,
289
+ maxHeight: e.maxHeight
288
290
  })
289
291
  }, null, 4)]));
290
292
  }
291
- }), [["__scopeId", "data-v-b0a66a4b"]]), E = { class: "flex flex-col gap-0" }, D = { class: "flex items-center gap-2" }, O = {
293
+ }), [["__scopeId", "data-v-05d72157"]]), D = { class: "flex flex-col gap-0" }, O = { class: "flex items-center gap-2" }, k = ["title"], A = ["title"], j = {
292
294
  key: 0,
293
295
  class: "px-3 py-1.5 text-body-small text-error"
294
- }, k = /* @__PURE__ */ c({
296
+ }, M = /* @__PURE__ */ l({
295
297
  __name: "MJsonEditor",
296
298
  props: {
297
299
  modelValue: {},
@@ -300,69 +302,81 @@ var y = { class: "overflow-hidden rounded-lg border border-outline-variant" }, b
300
302
  default: !1
301
303
  },
302
304
  minHeight: { default: "200px" },
303
- maxHeight: { default: "600px" }
305
+ maxHeight: { default: "600px" },
306
+ labels: {}
304
307
  },
305
308
  emits: ["update:modelValue"],
306
- setup(t, { emit: c }) {
307
- let u = t, d = c, f = m(JSON.stringify(u.modelValue, null, 2)), h = m(null), y = n(() => !h.value);
308
- _(() => u.modelValue, (e) => {
309
- let t = JSON.stringify(e, null, 2);
310
- t !== f.value && (f.value = t, h.value = null);
309
+ setup(n, { emit: l }) {
310
+ let d = e(), f = n, p = r(() => ({
311
+ valid: d.jsonValid,
312
+ invalid: d.jsonInvalid,
313
+ format: d.jsonFormat,
314
+ minify: d.jsonMinify,
315
+ ...f.labels
316
+ })), g = l, b = h(JSON.stringify(f.modelValue, null, 2)), x = h(null), S = r(() => !x.value);
317
+ v(() => f.modelValue, (e) => {
318
+ let t;
319
+ try {
320
+ t = JSON.parse(b.value);
321
+ } catch {
322
+ t = void 0;
323
+ }
324
+ JSON.stringify(t) !== JSON.stringify(e) && (b.value = JSON.stringify(e, null, 2), x.value = null);
311
325
  });
312
- function b(e) {
313
- f.value = e;
326
+ function C(e) {
327
+ b.value = e;
314
328
  try {
315
329
  let t = JSON.parse(e);
316
- h.value = null, d("update:modelValue", t);
330
+ x.value = null, g("update:modelValue", t);
317
331
  } catch (e) {
318
- h.value = e.message;
332
+ x.value = e.message;
319
333
  }
320
334
  }
321
- function x() {
335
+ function w() {
322
336
  try {
323
- let e = JSON.parse(f.value);
324
- f.value = JSON.stringify(e, null, 2), h.value = null, d("update:modelValue", e);
337
+ let e = JSON.parse(b.value);
338
+ b.value = JSON.stringify(e, null, 2), x.value = null, g("update:modelValue", e);
325
339
  } catch {}
326
340
  }
327
- function S() {
341
+ function T() {
328
342
  try {
329
- let e = JSON.parse(f.value);
330
- f.value = JSON.stringify(e), h.value = null, d("update:modelValue", e);
343
+ let e = JSON.parse(b.value);
344
+ b.value = JSON.stringify(e), x.value = null, g("update:modelValue", e);
331
345
  } catch {}
332
346
  }
333
- return (n, c) => (p(), i("div", E, [s(T, {
334
- "model-value": f.value,
347
+ return (e, r) => (m(), a("div", D, [c(E, {
348
+ "model-value": b.value,
335
349
  language: "json",
336
- readonly: t.readonly,
337
- "min-height": t.minHeight,
338
- "max-height": t.maxHeight,
339
- "onUpdate:modelValue": b
350
+ readonly: n.readonly,
351
+ "min-height": n.minHeight,
352
+ "max-height": n.maxHeight,
353
+ "onUpdate:modelValue": C
340
354
  }, {
341
- actions: v(() => [a("div", D, [
342
- a("span", { class: l(["flex items-center gap-1 rounded-full px-2.5 py-0.5 text-label-small", y.value ? "bg-success-container text-on-success-container" : "bg-error-container text-on-error-container"]) }, [s(e, {
343
- name: y.value ? "check_circle" : "error",
355
+ actions: y(() => [o("div", O, [
356
+ o("span", { class: u(["flex items-center gap-1 rounded-full px-2.5 py-0.5 text-label-small", S.value ? "bg-success-container text-on-success-container" : "bg-error-container text-on-error-container"]) }, [c(t, {
357
+ name: S.value ? "check_circle" : "error",
344
358
  size: 14
345
- }, null, 8, ["name"]), o(" " + g(y.value ? "Válido" : "Inválido"), 1)], 2),
346
- t.readonly ? r("", !0) : (p(), i("button", {
359
+ }, null, 8, ["name"]), s(" " + _(S.value ? p.value.valid : p.value.invalid), 1)], 2),
360
+ n.readonly ? i("", !0) : (m(), a("button", {
347
361
  key: 0,
348
362
  type: "button",
349
- title: "Formatear",
363
+ title: p.value.format,
350
364
  class: "flex h-7 cursor-pointer items-center gap-1 rounded px-2 text-label-medium text-on-surface-variant transition-colors hover:bg-on-surface/8",
351
- onClick: x
352
- }, [s(e, {
365
+ onClick: w
366
+ }, [c(t, {
353
367
  name: "format_indent_increase",
354
368
  size: 16
355
- }), c[0] ||= o(" Formatear ", -1)])),
356
- t.readonly ? r("", !0) : (p(), i("button", {
369
+ }), s(" " + _(p.value.format), 1)], 8, k)),
370
+ n.readonly ? i("", !0) : (m(), a("button", {
357
371
  key: 1,
358
372
  type: "button",
359
- title: "Minificar",
373
+ title: p.value.minify,
360
374
  class: "flex h-7 cursor-pointer items-center gap-1 rounded px-2 text-label-medium text-on-surface-variant transition-colors hover:bg-on-surface/8",
361
- onClick: S
362
- }, [s(e, {
375
+ onClick: T
376
+ }, [c(t, {
363
377
  name: "compress",
364
378
  size: 16
365
- }), c[1] ||= o(" Minificar ", -1)]))
379
+ }), s(" " + _(p.value.minify), 1)], 8, A))
366
380
  ])]),
367
381
  _: 1
368
382
  }, 8, [
@@ -370,10 +384,10 @@ var y = { class: "overflow-hidden rounded-lg border border-outline-variant" }, b
370
384
  "readonly",
371
385
  "min-height",
372
386
  "max-height"
373
- ]), h.value ? (p(), i("p", O, g(h.value), 1)) : r("", !0)]));
387
+ ]), x.value ? (m(), a("p", j, _(x.value), 1)) : i("", !0)]));
374
388
  }
375
389
  });
376
390
  //#endregion
377
- export { T as MCodeEditor, k as MJsonEditor };
391
+ export { E as MCodeEditor, M as MJsonEditor };
378
392
 
379
393
  //# sourceMappingURL=code-editor.js.map