@treeui/vue 0.12.0 → 0.14.0

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.
Files changed (94) hide show
  1. package/README.md +5 -1
  2. package/dist/components/TButton.cjs.map +1 -1
  3. package/dist/components/TButton.js.map +1 -1
  4. package/dist/components/TButton.vue.d.ts +2 -2
  5. package/dist/components/TButton.vue.d.ts.map +1 -1
  6. package/dist/components/TButton.vue_vue_type_script_setup_true_lang.cjs +1 -1
  7. package/dist/components/TButton.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  8. package/dist/components/TButton.vue_vue_type_script_setup_true_lang.js +2 -1
  9. package/dist/components/TButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  10. package/dist/components/TChart.cjs +2 -0
  11. package/dist/components/TChart.cjs.map +1 -0
  12. package/dist/components/TChart.js +7 -0
  13. package/dist/components/TChart.js.map +1 -0
  14. package/dist/components/TChart.vue.d.ts +123 -0
  15. package/dist/components/TChart.vue.d.ts.map +1 -0
  16. package/dist/components/TChart.vue_vue_type_script_setup_true_lang.cjs +2 -0
  17. package/dist/components/TChart.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  18. package/dist/components/TChart.vue_vue_type_script_setup_true_lang.js +370 -0
  19. package/dist/components/TChart.vue_vue_type_script_setup_true_lang.js.map +1 -0
  20. package/dist/components/TDonutChart.cjs +2 -0
  21. package/dist/components/TDonutChart.cjs.map +1 -0
  22. package/dist/components/TDonutChart.js +7 -0
  23. package/dist/components/TDonutChart.js.map +1 -0
  24. package/dist/components/TDonutChart.vue.d.ts +75 -0
  25. package/dist/components/TDonutChart.vue.d.ts.map +1 -0
  26. package/dist/components/TDonutChart.vue_vue_type_script_setup_true_lang.cjs +2 -0
  27. package/dist/components/TDonutChart.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  28. package/dist/components/TDonutChart.vue_vue_type_script_setup_true_lang.js +168 -0
  29. package/dist/components/TDonutChart.vue_vue_type_script_setup_true_lang.js.map +1 -0
  30. package/dist/components/TFileUpload.cjs.map +1 -1
  31. package/dist/components/TFileUpload.js.map +1 -1
  32. package/dist/components/TFileUpload.vue.d.ts +77 -10
  33. package/dist/components/TFileUpload.vue.d.ts.map +1 -1
  34. package/dist/components/TFileUpload.vue_vue_type_script_setup_true_lang.cjs +1 -1
  35. package/dist/components/TFileUpload.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  36. package/dist/components/TFileUpload.vue_vue_type_script_setup_true_lang.js +393 -144
  37. package/dist/components/TFileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
  38. package/dist/components/TGrid.vue.d.ts +1 -1
  39. package/dist/components/TGrid.vue.d.ts.map +1 -1
  40. package/dist/components/TNavMenu.cjs.map +1 -1
  41. package/dist/components/TNavMenu.js.map +1 -1
  42. package/dist/components/TNavMenu.vue.d.ts +9 -0
  43. package/dist/components/TNavMenu.vue.d.ts.map +1 -1
  44. package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.cjs +1 -1
  45. package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  46. package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.js +67 -55
  47. package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  48. package/dist/components/TPage.cjs +2 -0
  49. package/dist/components/TPage.cjs.map +1 -0
  50. package/dist/components/TPage.js +7 -0
  51. package/dist/components/TPage.js.map +1 -0
  52. package/dist/components/TPage.vue.d.ts +35 -0
  53. package/dist/components/TPage.vue.d.ts.map +1 -0
  54. package/dist/components/TPage.vue_vue_type_script_setup_true_lang.cjs +2 -0
  55. package/dist/components/TPage.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  56. package/dist/components/TPage.vue_vue_type_script_setup_true_lang.js +49 -0
  57. package/dist/components/TPage.vue_vue_type_script_setup_true_lang.js.map +1 -0
  58. package/dist/components/TPageHeader.cjs +2 -0
  59. package/dist/components/TPageHeader.cjs.map +1 -0
  60. package/dist/components/TPageHeader.js +7 -0
  61. package/dist/components/TPageHeader.js.map +1 -0
  62. package/dist/components/TPageHeader.vue.d.ts +40 -0
  63. package/dist/components/TPageHeader.vue.d.ts.map +1 -0
  64. package/dist/components/TPageHeader.vue_vue_type_script_setup_true_lang.cjs +2 -0
  65. package/dist/components/TPageHeader.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  66. package/dist/components/TPageHeader.vue_vue_type_script_setup_true_lang.js +54 -0
  67. package/dist/components/TPageHeader.vue_vue_type_script_setup_true_lang.js.map +1 -0
  68. package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.cjs +1 -1
  69. package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  70. package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.js +1 -4
  71. package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  72. package/dist/components/TSparkline.cjs +2 -0
  73. package/dist/components/TSparkline.cjs.map +1 -0
  74. package/dist/components/TSparkline.js +7 -0
  75. package/dist/components/TSparkline.js.map +1 -0
  76. package/dist/components/TSparkline.vue.d.ts +40 -0
  77. package/dist/components/TSparkline.vue.d.ts.map +1 -0
  78. package/dist/components/TSparkline.vue_vue_type_script_setup_true_lang.cjs +2 -0
  79. package/dist/components/TSparkline.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  80. package/dist/components/TSparkline.vue_vue_type_script_setup_true_lang.js +107 -0
  81. package/dist/components/TSparkline.vue_vue_type_script_setup_true_lang.js.map +1 -0
  82. package/dist/components/TStack.vue.d.ts +1 -1
  83. package/dist/components/TStack.vue.d.ts.map +1 -1
  84. package/dist/components/index.d.ts +9 -1
  85. package/dist/components/index.d.ts.map +1 -1
  86. package/dist/index.cjs +1 -1
  87. package/dist/index.js +51 -46
  88. package/dist/plugin.cjs +1 -1
  89. package/dist/plugin.cjs.map +1 -1
  90. package/dist/plugin.d.ts.map +1 -1
  91. package/dist/plugin.js +100 -90
  92. package/dist/plugin.js.map +1 -1
  93. package/dist/style.css +1 -1
  94. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"TFileUpload.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../src/components/TFileUpload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { createId, isActivationKey } from '@treeui/utils';\nimport { computed, onBeforeUnmount, onMounted, ref, useAttrs } from 'vue';\nimport type { TSize } from '../types/contracts';\nimport TSpinner from './TSpinner.vue';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport type TFileUploadRejectionReason =\n | 'file-invalid-type'\n | 'file-too-large'\n | 'too-many-files';\n\nexport interface TFileUploadRejection {\n file: File;\n reason: TFileUploadRejectionReason;\n message: string;\n}\n\nconst slots = defineSlots<{\n icon?: () => unknown;\n default?: (props: {\n files: File[];\n isDragActive: boolean;\n openFileDialog: () => void;\n clearFiles: () => void;\n }) => unknown;\n file?: (props: {\n file: File;\n index: number;\n removeFile: () => void;\n }) => unknown;\n actions?: (props: {\n files: File[];\n openFileDialog: () => void;\n clearFiles: () => void;\n }) => unknown;\n}>();\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: File[];\n size?: TSize;\n disabled?: boolean;\n loading?: boolean;\n invalid?: boolean;\n accept?: string;\n multiple?: boolean;\n maxFiles?: number | null;\n maxFileSize?: number | null;\n label?: string;\n description?: string;\n buttonLabel?: string;\n filesLabel?: string;\n paste?: boolean;\n drop?: boolean;\n showFileList?: boolean;\n }>(),\n {\n modelValue: () => [],\n size: 'md',\n disabled: false,\n loading: false,\n invalid: false,\n accept: '',\n multiple: true,\n label: 'Drop files here',\n description: 'Click to browse, drag and drop, or paste with Ctrl+V.',\n buttonLabel: 'Browse files',\n filesLabel: 'Selected files',\n maxFiles: null,\n maxFileSize: null,\n paste: true,\n drop: true,\n showFileList: true,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [files: File[]];\n 'files-accepted': [files: File[]];\n 'files-rejected': [rejections: TFileUploadRejection[]];\n}>();\n\nconst attrs = useAttrs();\nconst rootRef = ref<HTMLElement | null>(null);\nconst inputRef = ref<HTMLInputElement | null>(null);\nconst isDragActive = ref(false);\nconst isFocusedWithin = ref(false);\nconst dragDepth = ref(0);\nconst feedbackMessages = ref<string[]>([]);\n\nconst uploadId = createId('t-file-upload');\nconst descriptionId = `${uploadId}-description`;\nconst feedbackId = `${uploadId}-feedback`;\nconst filesId = `${uploadId}-files`;\n\nconst isUnavailable = computed(() => props.disabled || props.loading);\nconst hasFiles = computed(() => props.modelValue.length > 0);\nconst hasCustomBody = computed(() => Boolean(slots.default));\n\nconst effectiveMaxFiles = computed(() => {\n if (!props.multiple) {\n return 1;\n }\n\n if (typeof props.maxFiles !== 'number') {\n return Number.POSITIVE_INFINITY;\n }\n\n return Math.max(props.maxFiles, 0);\n});\n\nconst rootClasses = computed(() => [\n 't-file-upload',\n `t-file-upload--${props.size}`,\n {\n 'is-disabled': isUnavailable.value,\n 'is-invalid': props.invalid,\n 'is-loading': props.loading,\n 'is-drag-active': isDragActive.value,\n 'has-files': hasFiles.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst describedBy = computed(() => {\n const ids: string[] = [];\n\n if (props.description && !hasCustomBody.value) {\n ids.push(descriptionId);\n }\n\n if (feedbackMessages.value.length > 0) {\n ids.push(feedbackId);\n }\n\n if (props.showFileList && hasFiles.value) {\n ids.push(filesId);\n }\n\n return ids.length > 0 ? ids.join(' ') : undefined;\n});\n\nconst selectedFilesLabel = computed(() => `${props.filesLabel} (${props.modelValue.length})`);\n\nconst formatFileSize = (size: number) => {\n if (size < 1024) {\n return `${size} B`;\n }\n\n const units = ['KB', 'MB', 'GB'];\n let value = size / 1024;\n let unitIndex = 0;\n\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex += 1;\n }\n\n const roundedValue = value >= 10 || Number.isInteger(value)\n ? Math.round(value)\n : Number(value.toFixed(1));\n\n return `${roundedValue} ${units[unitIndex]}`;\n};\n\nconst fileKey = (file: File, index: number) =>\n `${file.name}-${file.size}-${file.lastModified}-${index}`;\n\nconst matchesAccept = (file: File) => {\n if (!props.accept.trim()) {\n return true;\n }\n\n const tokens = props.accept\n .split(',')\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean);\n\n const fileName = file.name.toLowerCase();\n const fileType = file.type.toLowerCase();\n\n return tokens.some((token) => {\n if (token.startsWith('.')) {\n return fileName.endsWith(token);\n }\n\n if (token.endsWith('/*')) {\n return fileType.startsWith(token.slice(0, -1));\n }\n\n return fileType === token;\n });\n};\n\nconst buildRejectionMessage = (\n file: File,\n reason: TFileUploadRejectionReason,\n) => {\n switch (reason) {\n case 'file-invalid-type':\n return `${file.name} is not an accepted file type.`;\n case 'file-too-large':\n return `${file.name} exceeds the ${formatFileSize(props.maxFileSize ?? 0)} limit.`;\n case 'too-many-files':\n return `You can only add up to ${effectiveMaxFiles.value} file${effectiveMaxFiles.value === 1 ? '' : 's'}.`;\n default:\n return `${file.name} could not be added.`;\n }\n};\n\nconst resetInputValue = () => {\n if (inputRef.value) {\n inputRef.value.value = '';\n }\n};\n\nconst openFileDialog = () => {\n if (isUnavailable.value) {\n return;\n }\n\n inputRef.value?.click();\n};\n\nconst emitFiles = (\n nextFiles: File[],\n acceptedFiles: File[],\n rejections: TFileUploadRejection[],\n) => {\n feedbackMessages.value = rejections.map((rejection) => rejection.message);\n\n if (rejections.length > 0) {\n emit('files-rejected', rejections);\n }\n\n if (acceptedFiles.length > 0) {\n emit('update:modelValue', nextFiles);\n emit('files-accepted', acceptedFiles);\n }\n};\n\nconst processFiles = (incomingFiles: File[]) => {\n if (isUnavailable.value || incomingFiles.length === 0) {\n return;\n }\n\n const nextFiles = props.multiple ? [...props.modelValue] : [];\n const acceptedFiles: File[] = [];\n const rejections: TFileUploadRejection[] = [];\n let remainingSlots = Number.isFinite(effectiveMaxFiles.value)\n ? Math.max(effectiveMaxFiles.value - nextFiles.length, 0)\n : Number.POSITIVE_INFINITY;\n\n for (const file of incomingFiles) {\n if (remainingSlots <= 0) {\n rejections.push({\n file,\n reason: 'too-many-files',\n message: buildRejectionMessage(file, 'too-many-files'),\n });\n continue;\n }\n\n if (!matchesAccept(file)) {\n rejections.push({\n file,\n reason: 'file-invalid-type',\n message: buildRejectionMessage(file, 'file-invalid-type'),\n });\n continue;\n }\n\n if (\n typeof props.maxFileSize === 'number' &&\n props.maxFileSize > 0 &&\n file.size > props.maxFileSize\n ) {\n rejections.push({\n file,\n reason: 'file-too-large',\n message: buildRejectionMessage(file, 'file-too-large'),\n });\n continue;\n }\n\n acceptedFiles.push(file);\n nextFiles.push(file);\n\n if (Number.isFinite(remainingSlots)) {\n remainingSlots -= 1;\n }\n }\n\n emitFiles(nextFiles, acceptedFiles, rejections);\n};\n\nconst removeFile = (index: number) => {\n if (isUnavailable.value) {\n return;\n }\n\n feedbackMessages.value = [];\n emit('update:modelValue', props.modelValue.filter((_, fileIndex) => fileIndex !== index));\n};\n\nconst clearFiles = () => {\n if (isUnavailable.value || !hasFiles.value) {\n return;\n }\n\n feedbackMessages.value = [];\n emit('update:modelValue', []);\n};\n\nconst hasTransferFiles = (dataTransfer?: DataTransfer | null) => {\n if (!dataTransfer) {\n return false;\n }\n\n return dataTransfer.files.length > 0 || Array.from(dataTransfer.types).includes('Files');\n};\n\nconst getClipboardFiles = (\n clipboardData?: Pick<DataTransfer, 'items'> | null,\n) => {\n if (!clipboardData?.items) {\n return [];\n }\n\n return Array.from(clipboardData.items)\n .map((item) => item.getAsFile?.() ?? null)\n .filter((file): file is File => file instanceof File);\n};\n\nconst onInputChange = (event: Event) => {\n const target = event.target as HTMLInputElement;\n processFiles(Array.from(target.files ?? []));\n resetInputValue();\n};\n\nconst onDropzoneKeydown = (event: KeyboardEvent) => {\n if (!isActivationKey(event) || isUnavailable.value) {\n return;\n }\n\n event.preventDefault();\n openFileDialog();\n};\n\nconst onFocusIn = () => {\n isFocusedWithin.value = true;\n};\n\nconst onFocusOut = (event: FocusEvent) => {\n const nextTarget = event.relatedTarget as Node | null;\n\n if (nextTarget && rootRef.value?.contains(nextTarget)) {\n return;\n }\n\n isFocusedWithin.value = false;\n};\n\nconst onPaste = (event: ClipboardEvent) => {\n if (!props.paste || isUnavailable.value) {\n return;\n }\n\n const clipboardFiles = getClipboardFiles(event.clipboardData);\n\n if (clipboardFiles.length === 0) {\n return;\n }\n\n event.preventDefault();\n processFiles(clipboardFiles);\n};\n\nconst onDocumentPaste = (event: Event) => {\n if (!isFocusedWithin.value || event.defaultPrevented) {\n return;\n }\n\n onPaste(event as ClipboardEvent);\n};\n\nconst onDragEnter = (event: DragEvent) => {\n if (!props.drop || isUnavailable.value || !hasTransferFiles(event.dataTransfer)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.value += 1;\n isDragActive.value = true;\n};\n\nconst onDragOver = (event: DragEvent) => {\n if (!props.drop || isUnavailable.value || !hasTransferFiles(event.dataTransfer)) {\n return;\n }\n\n event.preventDefault();\n\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'copy';\n }\n\n isDragActive.value = true;\n};\n\nconst onDragLeave = (event: DragEvent) => {\n if (!props.drop || isUnavailable.value || !hasTransferFiles(event.dataTransfer)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.value = Math.max(dragDepth.value - 1, 0);\n\n if (dragDepth.value === 0) {\n isDragActive.value = false;\n }\n};\n\nconst onDrop = (event: DragEvent) => {\n if (!props.drop || isUnavailable.value || !hasTransferFiles(event.dataTransfer)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.value = 0;\n isDragActive.value = false;\n processFiles(Array.from(event.dataTransfer?.files ?? []));\n};\n\nonMounted(() => {\n document.addEventListener('paste', onDocumentPaste);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('paste', onDocumentPaste);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n @paste=\"onPaste\"\n >\n <input\n :id=\"uploadId\"\n ref=\"inputRef\"\n class=\"t-visually-hidden\"\n type=\"file\"\n :accept=\"accept || undefined\"\n :multiple=\"multiple\"\n :disabled=\"isUnavailable\"\n @change=\"onInputChange\"\n >\n\n <div\n class=\"t-file-upload__dropzone\"\n :aria-busy=\"loading || undefined\"\n :aria-controls=\"showFileList && hasFiles ? filesId : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-disabled=\"isUnavailable || undefined\"\n :aria-invalid=\"invalid || undefined\"\n role=\"button\"\n :tabindex=\"isUnavailable ? -1 : 0\"\n @click=\"openFileDialog\"\n @keydown=\"onDropzoneKeydown\"\n @dragenter=\"onDragEnter\"\n @dragover=\"onDragOver\"\n @dragleave=\"onDragLeave\"\n @drop=\"onDrop\"\n >\n <div\n v-if=\"$slots.icon || !$slots.default\"\n class=\"t-file-upload__icon\"\n aria-hidden=\"true\"\n >\n <slot name=\"icon\">\n <span class=\"t-file-upload__icon-mark\">+</span>\n </slot>\n </div>\n\n <div class=\"t-file-upload__content\">\n <slot\n :files=\"modelValue\"\n :is-drag-active=\"isDragActive\"\n :open-file-dialog=\"openFileDialog\"\n :clear-files=\"clearFiles\"\n >\n <p class=\"t-file-upload__label\">\n {{ label }}\n </p>\n <p\n v-if=\"description\"\n :id=\"descriptionId\"\n class=\"t-file-upload__description\"\n >\n {{ description }}\n </p>\n <span class=\"t-file-upload__browse\">\n {{ buttonLabel }}\n </span>\n </slot>\n </div>\n\n <TSpinner\n v-if=\"loading\"\n class=\"t-file-upload__spinner\"\n size=\"sm\"\n label=\"Uploading files\"\n />\n </div>\n\n <ul\n v-if=\"feedbackMessages.length > 0\"\n :id=\"feedbackId\"\n class=\"t-file-upload__feedback\"\n aria-live=\"polite\"\n >\n <li\n v-for=\"message in feedbackMessages\"\n :key=\"message\"\n >\n {{ message }}\n </li>\n </ul>\n\n <div\n v-if=\"showFileList && hasFiles\"\n class=\"t-file-upload__files\"\n >\n <div class=\"t-file-upload__files-header\">\n <p\n :id=\"filesId\"\n class=\"t-file-upload__files-label\"\n >\n {{ selectedFilesLabel }}\n </p>\n\n <button\n class=\"t-file-upload__clear\"\n type=\"button\"\n :disabled=\"isUnavailable\"\n @click=\"clearFiles\"\n >\n Clear all\n </button>\n </div>\n\n <ul class=\"t-file-upload__list\">\n <li\n v-for=\"(file, index) in modelValue\"\n :key=\"fileKey(file, index)\"\n class=\"t-file-upload__file\"\n >\n <slot\n name=\"file\"\n :file=\"file\"\n :index=\"index\"\n :remove-file=\"() => removeFile(index)\"\n >\n <div class=\"t-file-upload__file-copy\">\n <p class=\"t-file-upload__file-name\">\n {{ file.name }}\n </p>\n <p class=\"t-file-upload__file-meta\">\n {{ formatFileSize(file.size) }}\n </p>\n </div>\n\n <button\n class=\"t-file-upload__remove\"\n type=\"button\"\n :disabled=\"isUnavailable\"\n :aria-label=\"`Remove ${file.name}`\"\n @click=\"removeFile(index)\"\n >\n Remove\n </button>\n </slot>\n </li>\n </ul>\n </div>\n\n <div\n v-if=\"$slots.actions\"\n class=\"t-file-upload__actions\"\n >\n <slot\n name=\"actions\"\n :files=\"modelValue\"\n :open-file-dialog=\"openFileDialog\"\n :clear-files=\"clearFiles\"\n />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"TFileUpload.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../src/components/TFileUpload.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { createId, isActivationKey } from '@treeui/utils';\nimport { computed, onBeforeUnmount, onMounted, ref, useAttrs, watch } from 'vue';\nimport type { TSize } from '../types/contracts';\nimport TProgress from './TProgress.vue';\nimport TSpinner from './TSpinner.vue';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport type TFileUploadRejectionReason =\n | 'file-invalid-type'\n | 'file-too-large'\n | 'too-many-files';\n\nexport interface TFileUploadRejection {\n file: File;\n reason: TFileUploadRejectionReason;\n message: string;\n}\n\nexport type TFileUploadStatus =\n | 'pending'\n | 'uploading'\n | 'paused'\n | 'error'\n | 'success';\n\nexport interface TFileUploadState {\n status: TFileUploadStatus;\n progress?: number;\n uploadedBytes?: number;\n remainingMs?: number;\n remainingLabel?: string;\n error?: string;\n resumable?: boolean;\n retryable?: boolean;\n thumbnailUrl?: string;\n meta?: string;\n}\n\nexport type TFileUploadStateSource =\n | ReadonlyMap<File, TFileUploadState>\n | ((file: File, index: number) => TFileUploadState | undefined);\n\nexport interface TFileUploadRetryPayload {\n file: File;\n index: number;\n fileKey: string;\n mode: 'resume' | 'restart';\n state: TFileUploadState;\n}\n\nexport interface TFileUploadCancelPayload {\n file: File;\n index: number;\n fileKey: string;\n state: TFileUploadState;\n reason: 'remove' | 'clear';\n}\n\nexport interface TFileUploadFileSlotProps {\n file: File;\n index: number;\n fileKey: string;\n state: TFileUploadState | undefined;\n thumbnailUrl: string | undefined;\n sizeLabel: string;\n typeLabel: string;\n dimensionsLabel: string | undefined;\n progressLabel: string | undefined;\n remainingLabel: string | undefined;\n uploadedLabel: string | undefined;\n statusLabel: string | undefined;\n actionLabel: string | undefined;\n canRetry: boolean;\n retryMode: 'resume' | 'restart';\n isDragActive: boolean;\n isDragReject: boolean;\n removeFile: () => void;\n retryFile: () => void;\n}\n\ninterface ThumbEntry {\n url: string;\n width: number | null;\n height: number | null;\n}\n\ninterface FileRow {\n slotProps: TFileUploadFileSlotProps;\n rowClass: string | undefined;\n metaText: string;\n statusText: string;\n errorText: string | undefined;\n errorId: string | undefined;\n showProgress: boolean;\n progressValue: number | undefined;\n progressAriaLabel: string;\n isBusy: boolean;\n isSuccess: boolean;\n}\n\nconst DEFAULT_STATUS_LABELS: Record<TFileUploadStatus, string> = {\n pending: 'Waiting',\n uploading: 'Uploading',\n paused: 'Paused',\n error: 'Upload failed',\n success: 'Uploaded',\n};\n\n// The status log keeps a bounded history of announcements. Old entries are\n// trimmed rather than replaced in place so that each terminal transition\n// inserts a brand new node: `aria-relevant` defaults to \"additions text\", so\n// assistive tech announces the insertion and ignores the trim.\nconst STATUS_LOG_LIMIT = 5;\n\ntype StatusLogEntry = {\n id: number;\n text: string;\n};\n\nconst slots = defineSlots<{\n icon?: () => unknown;\n default?: (props: {\n files: File[];\n isDragActive: boolean;\n isDragReject: boolean;\n openFileDialog: () => void;\n clearFiles: () => void;\n }) => unknown;\n file?: (props: TFileUploadFileSlotProps) => unknown;\n 'file-status'?: (props: TFileUploadFileSlotProps) => unknown;\n actions?: (props: {\n files: File[];\n openFileDialog: () => void;\n clearFiles: () => void;\n }) => unknown;\n}>();\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: File[];\n size?: TSize;\n disabled?: boolean;\n loading?: boolean;\n invalid?: boolean;\n accept?: string;\n multiple?: boolean;\n maxFiles?: number | null;\n maxFileSize?: number | null;\n label?: string;\n description?: string;\n buttonLabel?: string;\n filesLabel?: string;\n paste?: boolean;\n drop?: boolean;\n showFileList?: boolean;\n uploadState?: TFileUploadStateSource;\n dragLabel?: string;\n dragRejectLabel?: string;\n thumbnails?: boolean;\n maxThumbnailSize?: number | null;\n retryLabel?: string;\n resumeLabel?: (percent: number) => string;\n remainingTimeFormat?: (remainingMs: number) => string;\n statusLabels?: Partial<Record<TFileUploadStatus, string>>;\n }>(),\n {\n modelValue: () => [],\n size: 'md',\n disabled: false,\n loading: false,\n invalid: false,\n accept: '',\n multiple: true,\n label: 'Drop files here',\n description: 'Click to browse, drag and drop, or paste with Ctrl+V.',\n buttonLabel: 'Browse files',\n filesLabel: 'Selected files',\n maxFiles: null,\n maxFileSize: null,\n paste: true,\n drop: true,\n showFileList: true,\n uploadState: undefined,\n dragLabel: 'Release to upload',\n dragRejectLabel: 'This file type is not accepted',\n thumbnails: true,\n maxThumbnailSize: 10 * 1024 * 1024,\n retryLabel: 'Retry',\n resumeLabel: (percent: number) => `Resume from ${percent}%`,\n remainingTimeFormat: (remainingMs: number) => {\n if (remainingMs < 5000) {\n return 'Less than 5s left';\n }\n\n const seconds = Math.round(remainingMs / 1000);\n\n if (seconds < 60) {\n return `About ${seconds}s left`;\n }\n\n const minutes = Math.round(seconds / 60);\n\n if (minutes < 60) {\n return `About ${minutes} min left`;\n }\n\n return `About ${Math.round(minutes / 60)}h left`;\n },\n statusLabels: () => ({}),\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [files: File[]];\n 'files-accepted': [files: File[]];\n 'files-rejected': [rejections: TFileUploadRejection[]];\n retry: [payload: TFileUploadRetryPayload];\n cancel: [payload: TFileUploadCancelPayload];\n}>();\n\nconst attrs = useAttrs();\nconst rootRef = ref<HTMLElement | null>(null);\nconst inputRef = ref<HTMLInputElement | null>(null);\nconst isDragActive = ref(false);\nconst isDragReject = ref(false);\nconst isFocusedWithin = ref(false);\nconst dragDepth = ref(0);\nconst feedbackMessages = ref<string[]>([]);\nconst statusLog = ref<StatusLogEntry[]>([]);\nlet statusLogId = 0;\nconst retryFocusKey = ref<string | null>(null);\nconst thumbs = ref(new Map<File, ThumbEntry>());\n\nconst uploadId = createId('t-file-upload');\nconst descriptionId = `${uploadId}-description`;\nconst feedbackId = `${uploadId}-feedback`;\nconst filesId = `${uploadId}-files`;\n\nconst isDisabled = computed(() => props.disabled);\nconst hasFiles = computed(() => props.modelValue.length > 0);\nconst hasCustomBody = computed(() => Boolean(slots.default));\n\nconst resolvedStatusLabels = computed(() => ({\n ...DEFAULT_STATUS_LABELS,\n ...props.statusLabels,\n}));\n\nconst effectiveMaxFiles = computed(() => {\n if (!props.multiple) {\n return 1;\n }\n\n if (typeof props.maxFiles !== 'number') {\n return Number.POSITIVE_INFINITY;\n }\n\n return Math.max(props.maxFiles, 0);\n});\n\nconst rootClasses = computed(() => [\n 't-file-upload',\n `t-file-upload--${props.size}`,\n {\n 'is-disabled': isDisabled.value,\n 'is-invalid': props.invalid,\n 'is-loading': props.loading,\n 'is-drag-active': isDragActive.value,\n 'is-drag-reject': isDragReject.value,\n 'has-files': hasFiles.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst describedBy = computed(() => {\n const ids: string[] = [];\n\n if (props.description && !hasCustomBody.value) {\n ids.push(descriptionId);\n }\n\n if (feedbackMessages.value.length > 0) {\n ids.push(feedbackId);\n }\n\n if (props.showFileList && hasFiles.value) {\n ids.push(filesId);\n }\n\n return ids.length > 0 ? ids.join(' ') : undefined;\n});\n\nconst selectedFilesLabel = computed(() => `${props.filesLabel} (${props.modelValue.length})`);\n\nconst dropzoneLabel = computed(() => {\n if (isDragReject.value) {\n return props.dragRejectLabel;\n }\n\n return isDragActive.value ? props.dragLabel : props.label;\n});\n\nconst formatFileSize = (size: number) => {\n if (size < 1024) {\n return `${size} B`;\n }\n\n const units = ['KB', 'MB', 'GB'];\n let value = size / 1024;\n let unitIndex = 0;\n\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex += 1;\n }\n\n const roundedValue = value >= 10 || Number.isInteger(value)\n ? Math.round(value)\n : Number(value.toFixed(1));\n\n return `${roundedValue} ${units[unitIndex]}`;\n};\n\nconst formatFileType = (file: File) => {\n const dotIndex = file.name.lastIndexOf('.');\n const extension = dotIndex > 0 && dotIndex < file.name.length - 1\n ? file.name.slice(dotIndex + 1).toUpperCase()\n : '';\n\n if (extension && extension.length <= 5) {\n return extension;\n }\n\n const subtype = file.type.includes('/') ? file.type.split('/')[1] : '';\n\n return subtype ? subtype.toUpperCase() : 'FILE';\n};\n\nlet keySeq = 0;\nconst keyCache = new WeakMap<File, string>();\n\nconst fileKeyFor = (file: File) => {\n let key = keyCache.get(file);\n\n if (!key) {\n keySeq += 1;\n key = `${uploadId}-f${keySeq}`;\n keyCache.set(file, key);\n }\n\n return key;\n};\n\nconst acceptTokens = computed(() =>\n props.accept\n .split(',')\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean),\n);\n\nconst matchesAccept = (file: File) => {\n if (acceptTokens.value.length === 0) {\n return true;\n }\n\n const fileName = file.name.toLowerCase();\n const fileType = file.type.toLowerCase();\n\n return acceptTokens.value.some((token) => {\n if (token.startsWith('.')) {\n return fileName.endsWith(token);\n }\n\n if (token.endsWith('/*')) {\n return fileType.startsWith(token.slice(0, -1));\n }\n\n return fileType === token;\n });\n};\n\nconst canDetectDragReject = computed(() =>\n acceptTokens.value.length > 0 && acceptTokens.value.every((token) => token.includes('/')),\n);\n\nconst matchesAcceptType = (type: string) => {\n const fileType = type.toLowerCase();\n\n if (!fileType) {\n return true;\n }\n\n return acceptTokens.value.some((token) =>\n token.endsWith('/*') ? fileType.startsWith(token.slice(0, -1)) : fileType === token,\n );\n};\n\nconst detectDragReject = (dataTransfer?: DataTransfer | null) => {\n if (!canDetectDragReject.value || !dataTransfer) {\n return false;\n }\n\n const items = dataTransfer.items;\n\n if (!items || items.length === 0) {\n return false;\n }\n\n const fileItems = Array.from(items).filter((item) => item.kind === 'file');\n\n if (fileItems.length === 0) {\n return false;\n }\n\n return fileItems.every((item) => !matchesAcceptType(item.type));\n};\n\nconst resolveState = (file: File, index: number) => {\n const source = props.uploadState;\n\n if (!source) {\n return undefined;\n }\n\n return typeof source === 'function' ? source(file, index) : source.get(file);\n};\n\nconst resolvedStates = computed(() => props.modelValue.map(resolveState));\n\nconst canUseObjectUrl = () =>\n typeof window !== 'undefined' &&\n typeof URL !== 'undefined' &&\n typeof URL.createObjectURL === 'function' &&\n typeof URL.revokeObjectURL === 'function';\n\nconst isThumbnailable = (file: File) =>\n file.type.startsWith('image/') &&\n (props.maxThumbnailSize === null || file.size <= props.maxThumbnailSize);\n\nconst releaseAllThumbnails = () => {\n if (canUseObjectUrl()) {\n for (const entry of thumbs.value.values()) {\n URL.revokeObjectURL(entry.url);\n }\n }\n\n thumbs.value.clear();\n};\n\nconst syncThumbnails = () => {\n if (!props.thumbnails || !canUseObjectUrl()) {\n releaseAllThumbnails();\n return;\n }\n\n const live = new Set(props.modelValue);\n\n for (const [file, entry] of [...thumbs.value]) {\n if (!live.has(file)) {\n URL.revokeObjectURL(entry.url);\n thumbs.value.delete(file);\n }\n }\n\n for (const file of props.modelValue) {\n if (thumbs.value.has(file) || !isThumbnailable(file)) {\n continue;\n }\n\n const url = URL.createObjectURL(file);\n thumbs.value.set(file, { url, width: null, height: null });\n\n const image = new Image();\n\n image.onload = () => {\n const current = thumbs.value.get(file);\n\n if (!current || current.url !== url) {\n return;\n }\n\n thumbs.value.set(file, { url, width: image.naturalWidth, height: image.naturalHeight });\n };\n\n image.src = url;\n }\n};\n\nconst buildRejectionMessage = (\n file: File,\n reason: TFileUploadRejectionReason,\n) => {\n switch (reason) {\n case 'file-invalid-type':\n return `${file.name} is not an accepted file type.`;\n case 'file-too-large':\n return `${file.name} exceeds the ${formatFileSize(props.maxFileSize ?? 0)} limit.`;\n case 'too-many-files':\n return `You can only add up to ${effectiveMaxFiles.value} file${effectiveMaxFiles.value === 1 ? '' : 's'}.`;\n default:\n return `${file.name} could not be added.`;\n }\n};\n\nconst resetInputValue = () => {\n if (inputRef.value) {\n inputRef.value.value = '';\n }\n};\n\nconst openFileDialog = () => {\n if (isDisabled.value) {\n return;\n }\n\n inputRef.value?.click();\n};\n\nconst emitFiles = (\n nextFiles: File[],\n acceptedFiles: File[],\n rejections: TFileUploadRejection[],\n) => {\n feedbackMessages.value = rejections.map((rejection) => rejection.message);\n\n if (rejections.length > 0) {\n emit('files-rejected', rejections);\n }\n\n if (acceptedFiles.length > 0) {\n emit('update:modelValue', nextFiles);\n emit('files-accepted', acceptedFiles);\n }\n};\n\nconst processFiles = (incomingFiles: File[]) => {\n if (isDisabled.value || incomingFiles.length === 0) {\n return;\n }\n\n const nextFiles = props.multiple ? [...props.modelValue] : [];\n const acceptedFiles: File[] = [];\n const rejections: TFileUploadRejection[] = [];\n let remainingSlots = Number.isFinite(effectiveMaxFiles.value)\n ? Math.max(effectiveMaxFiles.value - nextFiles.length, 0)\n : Number.POSITIVE_INFINITY;\n\n for (const file of incomingFiles) {\n if (remainingSlots <= 0) {\n rejections.push({\n file,\n reason: 'too-many-files',\n message: buildRejectionMessage(file, 'too-many-files'),\n });\n continue;\n }\n\n if (!matchesAccept(file)) {\n rejections.push({\n file,\n reason: 'file-invalid-type',\n message: buildRejectionMessage(file, 'file-invalid-type'),\n });\n continue;\n }\n\n if (\n typeof props.maxFileSize === 'number' &&\n props.maxFileSize > 0 &&\n file.size > props.maxFileSize\n ) {\n rejections.push({\n file,\n reason: 'file-too-large',\n message: buildRejectionMessage(file, 'file-too-large'),\n });\n continue;\n }\n\n acceptedFiles.push(file);\n nextFiles.push(file);\n\n if (Number.isFinite(remainingSlots)) {\n remainingSlots -= 1;\n }\n }\n\n emitFiles(nextFiles, acceptedFiles, rejections);\n};\n\nconst emitCancel = (file: File, index: number, reason: 'remove' | 'clear') => {\n const state = resolvedStates.value[index];\n\n if (!state || (state.status !== 'uploading' && state.status !== 'paused')) {\n return;\n }\n\n emit('cancel', { file, index, fileKey: fileKeyFor(file), state, reason });\n};\n\nconst removeFile = (index: number) => {\n if (isDisabled.value) {\n return;\n }\n\n const file = props.modelValue[index];\n\n if (file) {\n emitCancel(file, index, 'remove');\n }\n\n feedbackMessages.value = [];\n emit('update:modelValue', props.modelValue.filter((_, fileIndex) => fileIndex !== index));\n};\n\nconst clearFiles = () => {\n if (isDisabled.value || !hasFiles.value) {\n return;\n }\n\n props.modelValue.forEach((file, index) => {\n emitCancel(file, index, 'clear');\n });\n\n feedbackMessages.value = [];\n emit('update:modelValue', []);\n};\n\nconst rows = computed<FileRow[]>(() =>\n props.modelValue.map((file, index) => {\n const state = resolvedStates.value[index];\n const status = state?.status;\n const thumb = thumbs.value.get(file);\n const key = fileKeyFor(file);\n\n const progress = state?.progress;\n const percent = typeof progress === 'number' ? Math.round(progress) : undefined;\n const isResumable = state?.resumable === true;\n\n const canRetry = Boolean(\n state && (status === 'error' || status === 'paused') && state.retryable !== false,\n );\n const retryMode: 'resume' | 'restart' = isResumable ? 'resume' : 'restart';\n\n const sizeLabel = formatFileSize(file.size);\n const typeLabel = formatFileType(file);\n const dimensionsLabel = thumb && thumb.width !== null && thumb.height !== null\n ? `${thumb.width} × ${thumb.height}`\n : undefined;\n\n const progressLabel = status === 'uploading' && percent !== undefined\n ? `${percent}%`\n : undefined;\n\n let remainingLabel: string | undefined;\n\n if (status === 'uploading') {\n if (state?.remainingLabel) {\n remainingLabel = state.remainingLabel;\n } else if (\n typeof state?.remainingMs === 'number' &&\n Number.isFinite(state.remainingMs) &&\n state.remainingMs >= 0\n ) {\n remainingLabel = props.remainingTimeFormat(state.remainingMs);\n }\n }\n\n const uploadedLabel = (status === 'error' || status === 'paused') &&\n typeof state?.uploadedBytes === 'number'\n ? `${formatFileSize(state.uploadedBytes)} of ${sizeLabel} uploaded`\n : undefined;\n\n const statusLabel = status ? resolvedStatusLabels.value[status] : undefined;\n\n let actionLabel: string | undefined;\n\n if (canRetry) {\n actionLabel = isResumable && percent !== undefined\n ? props.resumeLabel(percent)\n : props.retryLabel;\n }\n\n const errorText = status === 'error' ? state?.error : undefined;\n\n const slotProps: TFileUploadFileSlotProps = {\n file,\n index,\n fileKey: key,\n state,\n thumbnailUrl: state?.thumbnailUrl ?? thumb?.url,\n sizeLabel,\n typeLabel,\n dimensionsLabel,\n progressLabel,\n remainingLabel,\n uploadedLabel,\n statusLabel,\n actionLabel,\n canRetry,\n retryMode,\n isDragActive: isDragActive.value,\n isDragReject: isDragReject.value,\n removeFile: () => removeFile(index),\n retryFile: () => {\n if (!canRetry || isDisabled.value || !state) {\n return;\n }\n\n emit('retry', { file, index, fileKey: key, mode: retryMode, state });\n },\n };\n\n return {\n slotProps,\n rowClass: status ? `is-${status}` : undefined,\n metaText: [typeLabel, sizeLabel, dimensionsLabel, state?.meta].filter(Boolean).join(' · '),\n statusText: [statusLabel, progressLabel, remainingLabel, uploadedLabel]\n .filter(Boolean)\n .join(' · '),\n errorText,\n errorId: errorText ? `${key}-error` : undefined,\n showProgress: Boolean(\n state &&\n (status === 'uploading' ||\n status === 'paused' ||\n status === 'error' ||\n (status === 'success' && percent !== undefined)),\n ),\n progressValue: status === 'uploading' ? progress : (progress ?? 0),\n progressAriaLabel: `${statusLabel ?? ''} ${file.name}`.trim(),\n isBusy: status === 'uploading',\n isSuccess: status === 'success',\n };\n }),\n);\n\nconst hasTransferFiles = (dataTransfer?: DataTransfer | null) => {\n if (!dataTransfer) {\n return false;\n }\n\n return dataTransfer.files.length > 0 || Array.from(dataTransfer.types).includes('Files');\n};\n\nconst getClipboardFiles = (\n clipboardData?: Pick<DataTransfer, 'items'> | null,\n) => {\n if (!clipboardData?.items) {\n return [];\n }\n\n return Array.from(clipboardData.items)\n .map((item) => item.getAsFile?.() ?? null)\n .filter((file): file is File => file instanceof File);\n};\n\nconst onInputChange = (event: Event) => {\n const target = event.target as HTMLInputElement;\n processFiles(Array.from(target.files ?? []));\n resetInputValue();\n};\n\nconst onDropzoneKeydown = (event: KeyboardEvent) => {\n if (!isActivationKey(event) || isDisabled.value) {\n return;\n }\n\n event.preventDefault();\n openFileDialog();\n};\n\nconst onFocusIn = () => {\n isFocusedWithin.value = true;\n};\n\nconst onFocusOut = (event: FocusEvent) => {\n const nextTarget = event.relatedTarget as Node | null;\n\n if (nextTarget && rootRef.value?.contains(nextTarget)) {\n return;\n }\n\n isFocusedWithin.value = false;\n};\n\nconst onRetryFocus = (key: string) => {\n retryFocusKey.value = key;\n};\n\nconst onRetryBlur = (key: string) => {\n if (retryFocusKey.value === key) {\n retryFocusKey.value = null;\n }\n};\n\nconst onPaste = (event: ClipboardEvent) => {\n if (!props.paste || isDisabled.value) {\n return;\n }\n\n const clipboardFiles = getClipboardFiles(event.clipboardData);\n\n if (clipboardFiles.length === 0) {\n return;\n }\n\n event.preventDefault();\n processFiles(clipboardFiles);\n};\n\nconst onDocumentPaste = (event: Event) => {\n if (!isFocusedWithin.value || event.defaultPrevented) {\n return;\n }\n\n onPaste(event as ClipboardEvent);\n};\n\nconst onDragEnter = (event: DragEvent) => {\n if (!props.drop || isDisabled.value || !hasTransferFiles(event.dataTransfer)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.value += 1;\n isDragActive.value = true;\n isDragReject.value = detectDragReject(event.dataTransfer);\n};\n\nconst onDragOver = (event: DragEvent) => {\n if (!props.drop || isDisabled.value || !hasTransferFiles(event.dataTransfer)) {\n return;\n }\n\n event.preventDefault();\n\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'copy';\n }\n\n isDragActive.value = true;\n isDragReject.value = detectDragReject(event.dataTransfer);\n};\n\nconst onDragLeave = (event: DragEvent) => {\n if (!props.drop || isDisabled.value || !hasTransferFiles(event.dataTransfer)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.value = Math.max(dragDepth.value - 1, 0);\n\n if (dragDepth.value === 0) {\n isDragActive.value = false;\n isDragReject.value = false;\n }\n};\n\nconst onDrop = (event: DragEvent) => {\n if (!props.drop || isDisabled.value || !hasTransferFiles(event.dataTransfer)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.value = 0;\n isDragActive.value = false;\n isDragReject.value = false;\n processFiles(Array.from(event.dataTransfer?.files ?? []));\n};\n\nconst previousStatuses = new Map<File, TFileUploadStatus>();\n\nwatch(\n resolvedStates,\n (states) => {\n const messages: string[] = [];\n const live = new Set<File>();\n\n props.modelValue.forEach((file, index) => {\n live.add(file);\n\n const status = states[index]?.status;\n\n if (!status) {\n return;\n }\n\n const previous = previousStatuses.get(file);\n\n if (previous === status) {\n return;\n }\n\n previousStatuses.set(file, status);\n\n if (previous === undefined) {\n return;\n }\n\n if (status === 'success') {\n messages.push(`${file.name} uploaded.`);\n } else if (status === 'error') {\n messages.push(`${file.name} failed. ${states[index]?.error ?? ''}`.trim());\n }\n });\n\n for (const file of [...previousStatuses.keys()]) {\n if (!live.has(file)) {\n previousStatuses.delete(file);\n }\n }\n\n if (messages.length > 0) {\n // Append instead of overwriting a single string: two identical messages\n // in a row (a retry that fails with the same error) would otherwise write\n // a byte-identical value, which `Object.is` suppresses — no DOM mutation,\n // so `aria-live` never fires and the repeat failure is announced as silence.\n statusLogId += 1;\n statusLog.value = [\n ...statusLog.value,\n { id: statusLogId, text: messages.join(' ') },\n ].slice(-STATUS_LOG_LIMIT);\n }\n },\n { flush: 'post', immediate: true },\n);\n\nwatch(\n resolvedStates,\n () => {\n const key = retryFocusKey.value;\n const root = rootRef.value;\n\n if (!key || !root) {\n return;\n }\n\n if (document.activeElement && document.activeElement !== document.body) {\n return;\n }\n\n if (root.querySelector(`[data-t-retry=\"${key}\"]`)) {\n return;\n }\n\n retryFocusKey.value = null;\n\n const fallback = root.querySelector<HTMLElement>(`[data-t-remove=\"${key}\"]`) ??\n root.querySelector<HTMLElement>('.t-file-upload__dropzone');\n\n fallback?.focus();\n },\n { flush: 'post' },\n);\n\nwatch(() => props.modelValue, syncThumbnails);\nwatch(() => props.thumbnails, syncThumbnails);\n\nonMounted(() => {\n document.addEventListener('paste', onDocumentPaste);\n syncThumbnails();\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('paste', onDocumentPaste);\n releaseAllThumbnails();\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n @paste=\"onPaste\"\n >\n <input\n :id=\"uploadId\"\n ref=\"inputRef\"\n class=\"t-visually-hidden\"\n type=\"file\"\n :accept=\"accept || undefined\"\n :multiple=\"multiple\"\n :disabled=\"isDisabled\"\n @change=\"onInputChange\"\n >\n\n <div\n class=\"t-file-upload__dropzone\"\n :aria-busy=\"loading || undefined\"\n :aria-controls=\"showFileList && hasFiles ? filesId : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-disabled=\"isDisabled || undefined\"\n :aria-invalid=\"invalid || undefined\"\n role=\"button\"\n :tabindex=\"isDisabled ? -1 : 0\"\n @click=\"openFileDialog\"\n @keydown=\"onDropzoneKeydown\"\n @dragenter=\"onDragEnter\"\n @dragover=\"onDragOver\"\n @dragleave=\"onDragLeave\"\n @drop=\"onDrop\"\n >\n <div\n v-if=\"$slots.icon || !$slots.default\"\n class=\"t-file-upload__icon\"\n aria-hidden=\"true\"\n >\n <slot name=\"icon\">\n <span class=\"t-file-upload__icon-mark\">+</span>\n </slot>\n </div>\n\n <div class=\"t-file-upload__content\">\n <slot\n :files=\"modelValue\"\n :is-drag-active=\"isDragActive\"\n :is-drag-reject=\"isDragReject\"\n :open-file-dialog=\"openFileDialog\"\n :clear-files=\"clearFiles\"\n >\n <p class=\"t-file-upload__label\">\n {{ dropzoneLabel }}\n </p>\n <p\n v-if=\"description\"\n :id=\"descriptionId\"\n class=\"t-file-upload__description\"\n >\n {{ description }}\n </p>\n <span class=\"t-file-upload__browse\">\n {{ buttonLabel }}\n </span>\n </slot>\n </div>\n\n <TSpinner\n v-if=\"loading\"\n class=\"t-file-upload__spinner\"\n size=\"sm\"\n label=\"Uploading files\"\n />\n </div>\n\n <ul\n v-if=\"feedbackMessages.length > 0\"\n :id=\"feedbackId\"\n class=\"t-file-upload__feedback\"\n aria-live=\"polite\"\n >\n <li\n v-for=\"message in feedbackMessages\"\n :key=\"message\"\n >\n {{ message }}\n </li>\n </ul>\n\n <div\n class=\"t-file-upload__status-log t-visually-hidden\"\n role=\"log\"\n aria-live=\"polite\"\n >\n <p\n v-for=\"entry in statusLog\"\n :key=\"entry.id\"\n class=\"t-file-upload__status-log-entry\"\n >\n {{ entry.text }}\n </p>\n </div>\n\n <div\n v-if=\"showFileList && hasFiles\"\n class=\"t-file-upload__files\"\n >\n <div class=\"t-file-upload__files-header\">\n <p\n :id=\"filesId\"\n class=\"t-file-upload__files-label\"\n >\n {{ selectedFilesLabel }}\n </p>\n\n <button\n class=\"t-file-upload__clear\"\n type=\"button\"\n :disabled=\"isDisabled\"\n @click=\"clearFiles\"\n >\n Clear all\n </button>\n </div>\n\n <ul class=\"t-file-upload__list\">\n <li\n v-for=\"row in rows\"\n :key=\"row.slotProps.fileKey\"\n class=\"t-file-upload__file\"\n :class=\"row.rowClass\"\n :aria-busy=\"row.isBusy || undefined\"\n >\n <slot\n name=\"file\"\n v-bind=\"row.slotProps\"\n >\n <span\n class=\"t-file-upload__thumb\"\n aria-hidden=\"true\"\n >\n <img\n v-if=\"row.slotProps.thumbnailUrl\"\n class=\"t-file-upload__thumb-image\"\n :src=\"row.slotProps.thumbnailUrl\"\n alt=\"\"\n >\n <span\n v-else\n class=\"t-file-upload__thumb-fallback\"\n >\n {{ row.slotProps.typeLabel }}\n </span>\n </span>\n\n <div class=\"t-file-upload__file-copy\">\n <p class=\"t-file-upload__file-name\">\n {{ row.slotProps.file.name }}\n </p>\n <p class=\"t-file-upload__file-meta\">\n {{ row.metaText }}\n </p>\n\n <slot\n name=\"file-status\"\n v-bind=\"row.slotProps\"\n >\n <div\n v-if=\"row.showProgress\"\n class=\"t-file-upload__file-progress\"\n >\n <TProgress\n size=\"sm\"\n :value=\"row.progressValue\"\n :label=\"row.progressAriaLabel\"\n />\n </div>\n\n <p\n v-if=\"row.statusText\"\n class=\"t-file-upload__file-status\"\n >\n <span\n v-if=\"row.isSuccess\"\n class=\"t-file-upload__proof\"\n aria-hidden=\"true\"\n >✓</span>{{ row.statusText }}\n </p>\n\n <p\n v-if=\"row.errorText\"\n :id=\"row.errorId\"\n class=\"t-file-upload__file-error\"\n >\n {{ row.errorText }}\n </p>\n </slot>\n </div>\n\n <button\n v-if=\"row.slotProps.canRetry\"\n class=\"t-file-upload__retry\"\n type=\"button\"\n :data-t-retry=\"row.slotProps.fileKey\"\n :disabled=\"isDisabled\"\n :aria-describedby=\"row.errorId\"\n @focus=\"onRetryFocus(row.slotProps.fileKey)\"\n @blur=\"onRetryBlur(row.slotProps.fileKey)\"\n @click=\"row.slotProps.retryFile()\"\n >\n {{ row.slotProps.actionLabel }}\n </button>\n\n <button\n class=\"t-file-upload__remove\"\n type=\"button\"\n :data-t-remove=\"row.slotProps.fileKey\"\n :disabled=\"isDisabled\"\n :aria-label=\"`Remove ${row.slotProps.file.name}`\"\n @click=\"row.slotProps.removeFile()\"\n >\n Remove\n </button>\n </slot>\n </li>\n </ul>\n </div>\n\n <div\n v-if=\"$slots.actions\"\n class=\"t-file-upload__actions\"\n >\n <slot\n name=\"actions\"\n :files=\"modelValue\"\n :open-file-dialog=\"openFileDialog\"\n :clear-files=\"clearFiles\"\n />\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwGA,IAAM,KAA2D;GAC/D,SAAS;GACT,WAAW;GACX,QAAQ;GACR,OAAO;GACP,SAAS;EACX,GAaM,KAAQ,GAAA,GAkBR,IAAQ,GA2ER,IAAO,GAQP,IAAQ,GAAS,GACjB,IAAU,EAAwB,IAAI,GACtC,IAAW,EAA6B,IAAI,GAC5C,IAAe,EAAI,EAAK,GACxB,IAAe,EAAI,EAAK,GACxB,IAAkB,EAAI,EAAK,GAC3B,IAAY,EAAI,CAAC,GACjB,IAAmB,EAAc,CAAC,CAAC,GACnC,IAAY,EAAsB,CAAC,CAAC,GACtC,IAAc,GACZ,IAAgB,EAAmB,IAAI,GACvC,IAAS,kBAAI,IAAI,IAAsB,CAAC,GAExC,IAAW,GAAS,eAAe,GACnC,IAAgB,GAAG,EAAS,eAC5B,IAAa,GAAG,EAAS,YACzB,IAAU,GAAG,EAAS,SAEtB,IAAa,QAAe,EAAM,QAAQ,GAC1C,IAAW,QAAe,EAAM,WAAW,SAAS,CAAC,GACrD,KAAgB,QAAe,EAAQ,GAAM,OAAQ,GAErD,KAAuB,SAAgB;GAC3C,GAAG;GACH,GAAG,EAAM;EACX,EAAE,GAEI,IAAoB,QACnB,EAAM,WAIP,OAAO,EAAM,YAAa,WAIvB,KAAK,IAAI,EAAM,UAAU,CAAC,IAHxB,WAJA,CAQV,GAEK,KAAc,QAAe;GACjC;GACA,kBAAkB,EAAM;GACxB;IACE,eAAe,EAAW;IAC1B,cAAc,EAAM;IACpB,cAAc,EAAM;IACpB,kBAAkB,EAAa;IAC/B,kBAAkB,EAAa;IAC/B,aAAa,EAAS;GACxB;GACA,EAAM;EACR,CAAC,GAEK,KAAY,QAAe,EAAM,KAAK,GAEtC,KAAY,QAAe;GAC/B,IAAM,EAAE,OAAO,GAAQ,OAAO,GAAQ,GAAG,MAAS;GAClD,OAAO;EACT,CAAC,GAEK,KAAc,QAAe;GACjC,IAAM,IAAgB,CAAC;GAcvB,OAZI,EAAM,eAAe,CAAC,GAAc,SACtC,EAAI,KAAK,CAAa,GAGpB,EAAiB,MAAM,SAAS,KAClC,EAAI,KAAK,CAAU,GAGjB,EAAM,gBAAgB,EAAS,SACjC,EAAI,KAAK,CAAO,GAGX,EAAI,SAAS,IAAI,EAAI,KAAK,GAAG,IAAI,KAAA;EAC1C,CAAC,GAEK,KAAqB,QAAe,GAAG,EAAM,WAAW,IAAI,EAAM,WAAW,OAAO,EAAE,GAEtF,KAAgB,QAChB,EAAa,QACR,EAAM,kBAGR,EAAa,QAAQ,EAAM,YAAY,EAAM,KACrD,GAEK,KAAkB,MAAiB;GACvC,IAAI,IAAO,MACT,OAAO,GAAG,EAAK;GAGjB,IAAM,IAAQ;IAAC;IAAM;IAAM;GAAI,GAC3B,IAAQ,IAAO,MACf,IAAY;GAEhB,OAAO,KAAS,QAAQ,IAAY,EAAM,SAAS,IAEjD,AADA,KAAS,MACT,KAAa;GAOf,OAAO,GAJc,KAAS,MAAM,OAAO,UAAU,CAAK,IACtD,KAAK,MAAM,CAAK,IAChB,OAAO,EAAM,QAAQ,CAAC,CAAC,EAEJ,GAAG,EAAM;EAClC,GAEM,MAAkB,MAAe;GACrC,IAAM,IAAW,EAAK,KAAK,YAAY,GAAG,GACpC,IAAY,IAAW,KAAK,IAAW,EAAK,KAAK,SAAS,IAC5D,EAAK,KAAK,MAAM,IAAW,CAAC,CAAC,CAAC,YAAY,IAC1C;GAEJ,IAAI,KAAa,EAAU,UAAU,GACnC,OAAO;GAGT,IAAM,IAAU,EAAK,KAAK,SAAS,GAAG,IAAI,EAAK,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK;GAEpE,OAAO,IAAU,EAAQ,YAAY,IAAI;EAC3C,GAEI,KAAS,GACP,qBAAW,IAAI,QAAsB,GAErC,MAAc,MAAe;GACjC,IAAI,IAAM,GAAS,IAAI,CAAI;GAQ3B,OANK,MACH,MAAU,GACV,IAAM,GAAG,EAAS,IAAI,MACtB,GAAS,IAAI,GAAM,CAAG,IAGjB;EACT,GAEM,IAAe,QACnB,EAAM,OACH,MAAM,GAAG,CAAA,CACT,KAAK,MAAU,EAAM,KAAK,CAAC,CAAC,YAAY,CAAC,CAAA,CACzC,OAAO,OAAO,CACnB,GAEM,MAAiB,MAAe;GACpC,IAAI,EAAa,MAAM,WAAW,GAChC,OAAO;GAGT,IAAM,IAAW,EAAK,KAAK,YAAY,GACjC,IAAW,EAAK,KAAK,YAAY;GAEvC,OAAO,EAAa,MAAM,MAAM,MAC1B,EAAM,WAAW,GAAG,IACf,EAAS,SAAS,CAAK,IAG5B,EAAM,SAAS,IAAI,IACd,EAAS,WAAW,EAAM,MAAM,GAAG,EAAE,CAAC,IAGxC,MAAa,CACrB;EACH,GAEM,KAAsB,QAC1B,EAAa,MAAM,SAAS,KAAK,EAAa,MAAM,OAAO,MAAU,EAAM,SAAS,GAAG,CAAC,CAC1F,GAEM,MAAqB,MAAiB;GAC1C,IAAM,IAAW,EAAK,YAAY;GAMlC,OAJA,CAAK,KAIE,EAAa,MAAM,MAAM,MAC9B,EAAM,SAAS,IAAI,IAAI,EAAS,WAAW,EAAM,MAAM,GAAG,EAAE,CAAC,IAAI,MAAa,CAChF;EACF,GAEM,MAAoB,MAAuC;GAC/D,IAAI,CAAC,GAAoB,SAAS,CAAC,GACjC,OAAO;GAGT,IAAM,IAAQ,EAAa;GAE3B,IAAI,CAAC,KAAS,EAAM,WAAW,GAC7B,OAAO;GAGT,IAAM,IAAY,MAAM,KAAK,CAAK,CAAC,CAAC,QAAQ,MAAS,EAAK,SAAS,MAAM;GAMzE,OAJI,EAAU,WAAW,KAIlB,EAAU,OAAO,MAAS,CAAC,GAAkB,EAAK,IAAI,CAAC;EAChE,GAEM,MAAgB,GAAY,MAAkB;GAClD,IAAM,IAAS,EAAM;GAEhB,OAIL,OAAO,OAAO,KAAW,aAAa,EAAO,GAAM,CAAK,IAAI,EAAO,IAAI,CAAI;EAC7E,GAEM,IAAiB,QAAe,EAAM,WAAW,IAAI,EAAY,CAAC,GAElE,WACJ,OAAO,SAAW,OAClB,OAAO,MAAQ,OACf,OAAO,IAAI,mBAAoB,cAC/B,OAAO,IAAI,mBAAoB,YAE3B,MAAmB,MACvB,EAAK,KAAK,WAAW,QAAQ,MAC5B,EAAM,qBAAqB,QAAQ,EAAK,QAAQ,EAAM,mBAEnD,WAA6B;GACjC,IAAI,GAAgB,GAClB,KAAK,IAAM,KAAS,EAAO,MAAM,OAAO,GACtC,IAAI,gBAAgB,EAAM,GAAG;GAIjC,EAAO,MAAM,MAAM;EACrB,GAEM,UAAuB;GAC3B,IAAI,CAAC,EAAM,cAAc,CAAC,GAAgB,GAAG;IAC3C,GAAqB;IACrB;GACF;GAEA,IAAM,IAAO,IAAI,IAAI,EAAM,UAAU;GAErC,KAAK,IAAM,CAAC,GAAM,MAAU,CAAC,GAAG,EAAO,KAAK,GAC1C,AAAK,EAAK,IAAI,CAAI,MAChB,IAAI,gBAAgB,EAAM,GAAG,GAC7B,EAAO,MAAM,OAAO,CAAI;GAI5B,KAAK,IAAM,KAAQ,EAAM,YAAY;IACnC,IAAI,EAAO,MAAM,IAAI,CAAI,KAAK,CAAC,GAAgB,CAAI,GACjD;IAGF,IAAM,IAAM,IAAI,gBAAgB,CAAI;IACpC,EAAO,MAAM,IAAI,GAAM;KAAE;KAAK,OAAO;KAAM,QAAQ;IAAK,CAAC;IAEzD,IAAM,IAAQ,IAAI,MAAM;IAYxB,AAVA,EAAM,eAAe;KACnB,IAAM,IAAU,EAAO,MAAM,IAAI,CAAI;KAEjC,CAAC,KAAW,EAAQ,QAAQ,KAIhC,EAAO,MAAM,IAAI,GAAM;MAAE;MAAK,OAAO,EAAM;MAAc,QAAQ,EAAM;KAAc,CAAC;IACxF,GAEA,EAAM,MAAM;GACd;EACF,GAEM,KACJ,GACA,MACG;GACH,QAAQ,GAAR;IACE,KAAK,qBACH,OAAO,GAAG,EAAK,KAAK;IACtB,KAAK,kBACH,OAAO,GAAG,EAAK,KAAK,eAAe,EAAe,EAAM,eAAe,CAAC,EAAE;IAC5E,KAAK,kBACH,OAAO,0BAA0B,EAAkB,MAAM,OAAO,EAAkB,UAAU,IAAI,KAAK,IAAI;IAC3G,SACE,OAAO,GAAG,EAAK,KAAK;GACxB;EACF,GAEM,WAAwB;GAC5B,AAAI,EAAS,UACX,EAAS,MAAM,QAAQ;EAE3B,GAEM,UAAuB;GACvB,EAAW,SAIf,EAAS,OAAO,MAAM;EACxB,GAEM,MACJ,GACA,GACA,MACG;GAOH,AANA,EAAiB,QAAQ,EAAW,KAAK,MAAc,EAAU,OAAO,GAEpE,EAAW,SAAS,KACtB,EAAK,kBAAkB,CAAU,GAG/B,EAAc,SAAS,MACzB,EAAK,qBAAqB,CAAS,GACnC,EAAK,kBAAkB,CAAa;EAExC,GAEM,KAAgB,MAA0B;GAC9C,IAAI,EAAW,SAAS,EAAc,WAAW,GAC/C;GAGF,IAAM,IAAY,EAAM,WAAW,CAAC,GAAG,EAAM,UAAU,IAAI,CAAC,GACtD,IAAwB,CAAC,GACzB,IAAqC,CAAC,GACxC,IAAiB,OAAO,SAAS,EAAkB,KAAK,IACxD,KAAK,IAAI,EAAkB,QAAQ,EAAU,QAAQ,CAAC,IACtD;GAEJ,KAAK,IAAM,KAAQ,GAAe;IAChC,IAAI,KAAkB,GAAG;KACvB,EAAW,KAAK;MACd;MACA,QAAQ;MACR,SAAS,EAAsB,GAAM,gBAAgB;KACvD,CAAC;KACD;IACF;IAEA,IAAI,CAAC,GAAc,CAAI,GAAG;KACxB,EAAW,KAAK;MACd;MACA,QAAQ;MACR,SAAS,EAAsB,GAAM,mBAAmB;KAC1D,CAAC;KACD;IACF;IAEA,IACE,OAAO,EAAM,eAAgB,YAC7B,EAAM,cAAc,KACpB,EAAK,OAAO,EAAM,aAClB;KACA,EAAW,KAAK;MACd;MACA,QAAQ;MACR,SAAS,EAAsB,GAAM,gBAAgB;KACvD,CAAC;KACD;IACF;IAKA,AAHA,EAAc,KAAK,CAAI,GACvB,EAAU,KAAK,CAAI,GAEf,OAAO,SAAS,CAAc,KAChC;GAEJ;GAEA,GAAU,GAAW,GAAe,CAAU;EAChD,GAEM,MAAc,GAAY,GAAe,MAA+B;GAC5E,IAAM,IAAQ,EAAe,MAAM;GAE/B,CAAC,KAAU,EAAM,WAAW,eAAe,EAAM,WAAW,YAIhE,EAAK,UAAU;IAAE;IAAM;IAAO,SAAS,GAAW,CAAI;IAAG;IAAO;GAAO,CAAC;EAC1E,GAEM,MAAc,MAAkB;GACpC,IAAI,EAAW,OACb;GAGF,IAAM,IAAO,EAAM,WAAW;GAO9B,AALI,KACF,GAAW,GAAM,GAAO,QAAQ,GAGlC,EAAiB,QAAQ,CAAC,GAC1B,EAAK,qBAAqB,EAAM,WAAW,QAAQ,GAAG,MAAc,MAAc,CAAK,CAAC;EAC1F,GAEM,UAAmB;GACnB,EAAW,SAAS,CAAC,EAAS,UAIlC,EAAM,WAAW,SAAS,GAAM,MAAU;IACxC,GAAW,GAAM,GAAO,OAAO;GACjC,CAAC,GAED,EAAiB,QAAQ,CAAC,GAC1B,EAAK,qBAAqB,CAAC,CAAC;EAC9B,GAEM,KAAO,QACX,EAAM,WAAW,KAAK,GAAM,MAAU;GACpC,IAAM,IAAQ,EAAe,MAAM,IAC7B,IAAS,GAAO,QAChB,IAAQ,EAAO,MAAM,IAAI,CAAI,GAC7B,IAAM,GAAW,CAAI,GAErB,IAAW,GAAO,UAClB,IAAU,OAAO,KAAa,WAAW,KAAK,MAAM,CAAQ,IAAI,KAAA,GAChE,IAAc,GAAO,cAAc,IAEnC,IAAW,GACf,MAAU,MAAW,WAAW,MAAW,aAAa,EAAM,cAAc,KAExE,IAAkC,IAAc,WAAW,WAE3D,IAAY,EAAe,EAAK,IAAI,GACpC,IAAY,GAAe,CAAI,GAC/B,IAAkB,KAAS,EAAM,UAAU,QAAQ,EAAM,WAAW,OACtE,GAAG,EAAM,MAAM,KAAK,EAAM,WAC1B,KAAA,GAEE,IAAgB,MAAW,eAAe,MAAY,KAAA,IACxD,GAAG,EAAQ,KACX,KAAA,GAEA;GAEJ,AAAI,MAAW,gBACT,GAAO,iBACT,IAAiB,EAAM,iBAEvB,OAAO,GAAO,eAAgB,YAC9B,OAAO,SAAS,EAAM,WAAW,KACjC,EAAM,eAAe,MAErB,IAAiB,EAAM,oBAAoB,EAAM,WAAW;GAIhE,IAAM,KAAiB,MAAW,WAAW,MAAW,aACtD,OAAO,GAAO,iBAAkB,WAC9B,GAAG,EAAe,EAAM,aAAa,EAAE,MAAM,EAAU,aACvD,KAAA,GAEE,IAAc,IAAS,GAAqB,MAAM,KAAU,KAAA,GAE9D;GAEJ,AAAI,MACF,IAAc,KAAe,MAAY,KAAA,IACrC,EAAM,YAAY,CAAO,IACzB,EAAM;GAGZ,IAAM,IAAY,MAAW,UAAU,GAAO,QAAQ,KAAA;GA8BtD,OAAO;IACL,WAAA;KA5BA;KACA;KACA,SAAS;KACT;KACA,cAAc,GAAO,gBAAgB,GAAO;KAC5C;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA,cAAc,EAAa;KAC3B,cAAc,EAAa;KAC3B,kBAAkB,GAAW,CAAK;KAClC,iBAAiB;MACX,CAAC,KAAY,EAAW,SAAS,CAAC,KAItC,EAAK,SAAS;OAAE;OAAM;OAAO,SAAS;OAAK,MAAM;OAAW;MAAM,CAAC;KACrE;IAIA;IACA,UAAU,IAAS,MAAM,MAAW,KAAA;IACpC,UAAU;KAAC;KAAW;KAAW;KAAiB,GAAO;IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;IACzF,YAAY;KAAC;KAAa;KAAe;KAAgB;IAAa,CAAA,CACnE,OAAO,OAAO,CAAA,CACd,KAAK,KAAK;IACb;IACA,SAAS,IAAY,GAAG,EAAI,UAAU,KAAA;IACtC,cAAc,GACZ,MACC,MAAW,eACV,MAAW,YACX,MAAW,WACV,MAAW,aAAa,MAAY,KAAA;IAEzC,eAAe,MAAW,cAAc,IAAY,KAAY;IAChE,mBAAmB,GAAG,KAAe,GAAG,GAAG,EAAK,OAAO,KAAK;IAC5D,QAAQ,MAAW;IACnB,WAAW,MAAW;GACxB;EACF,CAAC,CACH,GAEM,KAAoB,MACnB,IAIE,EAAa,MAAM,SAAS,KAAK,MAAM,KAAK,EAAa,KAAK,CAAC,CAAC,SAAS,OAAO,IAH9E,IAML,MACJ,MAEK,GAAe,QAIb,MAAM,KAAK,EAAc,KAAK,CAAA,CAClC,KAAK,MAAS,EAAK,YAAY,KAAK,IAAI,CAAA,CACxC,QAAQ,MAAuB,aAAgB,IAAI,IAL7C,CAAC,GAQN,MAAiB,MAAiB;GACtC,IAAM,IAAS,EAAM;GAErB,AADA,EAAa,MAAM,KAAK,EAAO,SAAS,CAAC,CAAC,CAAC,GAC3C,GAAgB;EAClB,GAEM,MAAqB,MAAyB;GAC9C,CAAC,GAAgB,CAAK,KAAK,EAAW,UAI1C,EAAM,eAAe,GACrB,EAAe;EACjB,GAEM,WAAkB;GACtB,EAAgB,QAAQ;EAC1B,GAEM,MAAc,MAAsB;GACxC,IAAM,IAAa,EAAM;GAErB,KAAc,EAAQ,OAAO,SAAS,CAAU,MAIpD,EAAgB,QAAQ;EAC1B,GAEM,MAAgB,MAAgB;GACpC,EAAc,QAAQ;EACxB,GAEM,MAAe,MAAgB;GACnC,AAAI,EAAc,UAAU,MAC1B,EAAc,QAAQ;EAE1B,GAEM,MAAW,MAA0B;GACzC,IAAI,CAAC,EAAM,SAAS,EAAW,OAC7B;GAGF,IAAM,IAAiB,GAAkB,EAAM,aAAa;GAExD,EAAe,WAAW,MAI9B,EAAM,eAAe,GACrB,EAAa,CAAc;EAC7B,GAEM,KAAmB,MAAiB;GACpC,CAAC,EAAgB,SAAS,EAAM,oBAIpC,GAAQ,CAAuB;EACjC,GAEM,MAAe,MAAqB;GACpC,CAAC,EAAM,QAAQ,EAAW,SAAS,CAAC,EAAiB,EAAM,YAAY,MAI3E,EAAM,eAAe,GACrB,EAAU,SAAS,GACnB,EAAa,QAAQ,IACrB,EAAa,QAAQ,GAAiB,EAAM,YAAY;EAC1D,GAEM,MAAc,MAAqB;GACnC,CAAC,EAAM,QAAQ,EAAW,SAAS,CAAC,EAAiB,EAAM,YAAY,MAI3E,EAAM,eAAe,GAEjB,EAAM,iBACR,EAAM,aAAa,aAAa,SAGlC,EAAa,QAAQ,IACrB,EAAa,QAAQ,GAAiB,EAAM,YAAY;EAC1D,GAEM,MAAe,MAAqB;GACpC,CAAC,EAAM,QAAQ,EAAW,SAAS,CAAC,EAAiB,EAAM,YAAY,MAI3E,EAAM,eAAe,GACrB,EAAU,QAAQ,KAAK,IAAI,EAAU,QAAQ,GAAG,CAAC,GAE7C,EAAU,UAAU,MACtB,EAAa,QAAQ,IACrB,EAAa,QAAQ;EAEzB,GAEM,MAAU,MAAqB;GAC/B,CAAC,EAAM,QAAQ,EAAW,SAAS,CAAC,EAAiB,EAAM,YAAY,MAI3E,EAAM,eAAe,GACrB,EAAU,QAAQ,GAClB,EAAa,QAAQ,IACrB,EAAa,QAAQ,IACrB,EAAa,MAAM,KAAK,EAAM,cAAc,SAAS,CAAC,CAAC,CAAC;EAC1D,GAEM,oBAAmB,IAAI,IAA6B;SAE1D,EACE,IACC,MAAW;GACV,IAAM,IAAqB,CAAC,GACtB,oBAAO,IAAI,IAAU;GAE3B,EAAM,WAAW,SAAS,GAAM,MAAU;IACxC,EAAK,IAAI,CAAI;IAEb,IAAM,IAAS,EAAO,EAAM,EAAE;IAE9B,IAAI,CAAC,GACH;IAGF,IAAM,IAAW,EAAiB,IAAI,CAAI;IAEtC,MAAa,MAIjB,EAAiB,IAAI,GAAM,CAAM,GAE7B,MAAa,KAAA,MAIb,MAAW,YACb,EAAS,KAAK,GAAG,EAAK,KAAK,WAAW,IAC7B,MAAW,WACpB,EAAS,KAAK,GAAG,EAAK,KAAK,WAAW,EAAO,EAAM,EAAE,SAAS,KAAK,KAAK,CAAC;GAE7E,CAAC;GAED,KAAK,IAAM,KAAQ,CAAC,GAAG,EAAiB,KAAK,CAAC,GAC5C,AAAK,EAAK,IAAI,CAAI,KAChB,EAAiB,OAAO,CAAI;GAIhC,AAAI,EAAS,SAAS,MAKpB,KAAe,GACf,EAAU,QAAQ,CAChB,GAAG,EAAU,OACb;IAAE,IAAI;IAAa,MAAM,EAAS,KAAK,GAAG;GAAE,CAC9C,CAAC,CAAC,MAAM,EAAiB;EAE7B,GACA;GAAE,OAAO;GAAQ,WAAW;EAAK,CACnC,GAEA,EACE,SACM;GACJ,IAAM,IAAM,EAAc,OACpB,IAAO,EAAQ;GAEjB,CAAC,KAAO,CAAC,KAIT,SAAS,iBAAiB,SAAS,kBAAkB,SAAS,QAI9D,EAAK,cAAc,kBAAkB,EAAI,GAAG,MAIhD,EAAc,QAAQ,OAEL,EAAK,cAA2B,mBAAmB,EAAI,GAAG,KACzE,EAAK,cAA2B,0BAA0B,EAAA,EAElD,MAAM;EAClB,GACA,EAAE,OAAO,OAAO,CAClB,GAEA,QAAY,EAAM,YAAY,CAAc,GAC5C,QAAY,EAAM,YAAY,CAAc,GAE5C,QAAgB;GAEd,AADA,SAAS,iBAAiB,SAAS,CAAe,GAClD,EAAe;EACjB,CAAC,GAED,QAAsB;GAEpB,AADA,SAAS,oBAAoB,SAAS,CAAe,GACrD,GAAqB;EACvB,CAAC,mBAIC,EAkPM,OAlPN,EAkPM;YAjPA;GAAJ,KAAI;KACI,GAAA,OAAS;GAChB,OAAO,GAAA;GACP,OAAO,GAAA;GACP,WAAS;GACT,YAAU;GACH;;GAER,EASC,SAAA;IARE,IAAI,GAAA,CAAA;aACD;IAAJ,KAAI;IACJ,OAAM;IACN,MAAK;IACJ,QAAQ,EAAA,UAAU,KAAA;IAClB,UAAU,EAAA;IACV,UAAU,EAAA;IACV,UAAQ;;GAGX,EAwDM,OAAA;IAvDJ,OAAM;IACL,aAAW,EAAA,WAAW,KAAA;IACtB,iBAAe,EAAA,gBAAgB,EAAA,QAAW,IAAU,KAAA;IACpD,oBAAkB,GAAA;IAClB,iBAAe,EAAA,SAAc,KAAA;IAC7B,gBAAc,EAAA,WAAW,KAAA;IAC1B,MAAK;IACJ,UAAU,EAAA,QAAU,KAAA;IACpB,SAAO;IACP,WAAS;IACT,aAAW;IACX,YAAU;IACV,aAAW;IACL;;IAGCA,EAAAA,OAAO,QAAI,CAAKA,EAAAA,OAAO,WAAA,EAAA,GAD/B,EAQM,OARN,IAQM,CAHJ,EAEO,EAAA,QAAA,QAAA,CAAA,SAAA,CAAA,AAAA,EAAA,OADL,EAA+C,QAAA,EAAzC,OAAM,2BAA0B,GAAC,KAAC,EAAA,CAAA,CAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;IAI5C,EAsBM,OAtBN,IAsBM,CArBJ,EAoBO,EAAA,QAAA,WAAA;KAnBJ,OAAO,EAAA;KACP,cAAgB,EAAA;KAChB,cAAgB,EAAA;KACE;KACL;aAeT;KAbL,EAEI,KAFJ,IAEI,EADC,GAAA,KAAa,GAAA,CAAA;KAGV,EAAA,eAAA,EAAA,GADR,EAMI,KAAA;;MAJD,IAAI;MACL,OAAM;UAEH,EAAA,WAAW,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA;KAEhB,EAEO,QAFP,IAEO,EADF,EAAA,WAAW,GAAA,CAAA;;IAMZ,EAAA,WAAA,EAAA,GADR,EAKE,GAAA;;KAHA,OAAM;KACN,MAAK;KACL,OAAM;;;GAKF,EAAA,MAAiB,SAAM,KAAA,EAAA,GAD/B,EAYK,MAAA;;IAVF,IAAI;IACL,OAAM;IACN,aAAU;eAEV,EAKK,GAAA,MAAA,EAJe,EAAA,QAAX,YADT,EAKK,MAAA,EAHF,KAAK,EAAO,GAAA,EAEV,CAAO,GAAA,CAAA;GAId,EAYM,OAZN,IAYM,EAAA,EAAA,EAAA,GAPJ,EAMI,GAAA,MAAA,EALc,EAAA,QAAT,YADT,EAMI,KAAA;IAJD,KAAK,EAAM;IACZ,OAAM;QAEH,EAAM,IAAI,GAAA,CAAA;GAKT,EAAA,gBAAgB,EAAA,SAAA,EAAA,GADxB,EA2HM,OA3HN,IA2HM,CAvHJ,EAgBM,OAhBN,IAgBM,CAfJ,EAKI,KAAA;IAJD,IAAI;IACL,OAAM;QAEH,GAAA,KAAkB,GAAA,CAAA,GAGvB,EAOS,UAAA;IANP,OAAM;IACN,MAAK;IACJ,UAAU,EAAA;IACV,SAAO;MACT,eAED,GAAA,EAAA,CAAA,CAAA,GAGF,EAoGK,MApGL,IAoGK,EAAA,EAAA,EAAA,GAnGH,EAkGK,GAAA,MAAA,EAjGW,GAAA,QAAP,YADT,EAkGK,MAAA;IAhGF,KAAK,EAAI,UAAU;IACpB,OAAK,EAAA,CAAC,uBACE,EAAI,QAAQ,CAAA;IACnB,aAAW,EAAI,UAAU,KAAA;OAE1B,EA0FO,EAAA,QAAA,QA1FP,EA0FO,EAAA,SAAA,GAAA,GAxFG,EAAI,SAAS,SAwFhB;IAtFL,EAgBO,QAhBP,IAgBO,CAXG,EAAI,UAAU,gBAAA,EAAA,GADtB,EAKC,OAAA;;KAHC,OAAM;KACL,KAAK,EAAI,UAAU;KACpB,KAAI;6BAEN,EAKO,QALP,IAKO,EADF,EAAI,UAAU,SAAS,GAAA,CAAA,EAAA,CAAA;IAI9B,EA0CM,OA1CN,IA0CM;KAzCJ,EAEI,KAFJ,IAEI,EADC,EAAI,UAAU,KAAK,IAAI,GAAA,CAAA;KAE5B,EAEI,KAFJ,IAEI,EADC,EAAI,QAAQ,GAAA,CAAA;KAGjB,EAiCO,EAAA,QAAA,eAjCP,EAiCO,EAAA,SAAA,GAAA,GA/BG,EAAI,SAAS,SA+BhB;MA5BG,EAAI,gBAAA,EAAA,GADZ,EASM,OATN,IASM,CALJ,EAIE,GAAA;OAHA,MAAK;OACJ,OAAO,EAAI;OACX,OAAO,EAAI;;MAKR,EAAI,cAAA,EAAA,GADZ,EASI,KATJ,IASI,CAJM,EAAI,aAAA,EAAA,GADZ,EAIS,QAJT,IAIC,GAAC,KAAA,EAAA,IAAA,EAAA,GAAA,EAAA,EAAU,EAAI,UAAU,GAAA,CAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;MAIpB,EAAI,aAAA,EAAA,GADZ,EAMI,KAAA;;OAJD,IAAI,EAAI;OACT,OAAM;WAEH,EAAI,SAAS,GAAA,GAAA,EAAA,KAAA,EAAA,IAAA,EAAA;;;IAMd,EAAI,UAAU,YAAA,EAAA,GADtB,EAYS,UAAA;;KAVP,OAAM;KACN,MAAK;KACJ,gBAAc,EAAI,UAAU;KAC5B,UAAU,EAAA;KACV,oBAAkB,EAAI;KACtB,UAAK,MAAE,GAAa,EAAI,UAAU,OAAO;KACzC,SAAI,MAAE,GAAY,EAAI,UAAU,OAAO;KACvC,UAAK,MAAE,EAAI,UAAU,UAAS;SAE5B,EAAI,UAAU,WAAW,GAAA,IAAA,EAAA,KAAA,EAAA,IAAA,EAAA;IAG9B,EASS,UAAA;KARP,OAAM;KACN,MAAK;KACJ,iBAAe,EAAI,UAAU;KAC7B,UAAU,EAAA;KACV,cAAU,UAAY,EAAI,UAAU,KAAK;KACzC,UAAK,MAAE,EAAI,UAAU,WAAU;OACjC,YAED,GAAA,EAAA;;GAOAA,EAAAA,OAAO,WAAA,EAAA,GADf,EAUM,OAVN,IAUM,CANJ,EAKE,EAAA,QAAA,WAAA;IAHC,OAAO,EAAA;IACW;IACL"}
@@ -18,11 +18,11 @@ declare function __VLS_template(): {
18
18
  };
19
19
  type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
20
20
  declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
21
+ gap: string;
21
22
  as: string;
22
23
  align: string;
23
24
  columns: number;
24
25
  minItemWidth: string;
25
- gap: string;
26
26
  rowGap: string;
27
27
  justify: string;
28
28
  dense: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"TGrid.vue.d.ts","sourceRoot":"","sources":["../../src/components/TGrid.vue"],"names":[],"mappings":"AA+EA,KAAK,WAAW,GAAG;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAyDJ,iBAAS,cAAc;WAkCT,OAAO,IAA6B;;yBAVrB,GAAG;;;;EAe/B;AAYD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;QArHZ,MAAM;WAKH,MAAM;aAJJ,MAAM;kBACD,MAAM;SACf,MAAM;YACH,MAAM;aAEL,MAAM;WACR,OAAO;6EAqHjB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"TGrid.vue.d.ts","sourceRoot":"","sources":["../../src/components/TGrid.vue"],"names":[],"mappings":"AA+EA,KAAK,WAAW,GAAG;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAyDJ,iBAAS,cAAc;WAkCT,OAAO,IAA6B;;yBAVrB,GAAG;;;;EAe/B;AAYD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;SAlHX,MAAM;QAHP,MAAM;WAKH,MAAM;aAJJ,MAAM;kBACD,MAAM;YAEZ,MAAM;aAEL,MAAM;WACR,OAAO;6EAqHjB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"TNavMenu.cjs","names":[],"sources":["../../src/components/TNavMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { getNextEnabledIndex, isActivationKey } from '@treeui/utils';\nimport { computed, getCurrentInstance, inject, nextTick, ref, useAttrs, watch, type Component, type ComponentPublicInstance } from 'vue';\nimport type { TSize } from '../types/contracts';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport interface TNavMenuItem {\n label: string;\n value: string;\n shortLabel?: string;\n description?: string;\n icon?: Component;\n to?: string | Record<string, unknown>;\n badge?: string | number;\n disabled?: boolean;\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n defaultValue?: string;\n items?: TNavMenuItem[];\n size?: TSize;\n collapsed?: boolean;\n disabled?: boolean;\n }>(),\n {\n modelValue: undefined,\n defaultValue: '',\n items: () => [],\n size: 'md',\n collapsed: undefined,\n disabled: false,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n select: [value: string, item: TNavMenuItem];\n}>();\n\ndefineSlots<{\n item?: (props: {\n item: TNavMenuItem;\n selected: boolean;\n focused: boolean;\n collapsed: boolean;\n }) => unknown;\n empty?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebar = inject(treeSidebarInjectionKey, null);\nconst internalValue = ref(props.defaultValue);\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map());\nconst instance = getCurrentInstance();\n\nconst selectedValue = computed(() => props.modelValue ?? internalValue.value);\nconst resolvedCollapsed = computed(() => props.collapsed ?? sidebar?.collapsed.value ?? false);\n\nconst routerLinkComponent = computed<Component | null>(() => (\n (instance?.appContext.components.RouterLink as Component | undefined) ?? null\n));\n\nconst itemTag = (item: TNavMenuItem) => {\n if (item.to && routerLinkComponent.value) {\n return routerLinkComponent.value;\n }\n return 'button';\n};\n\nconst getInitialFocusedIndex = () => {\n const selectedIndex = props.items.findIndex((item) =>\n item.value === selectedValue.value && !item.disabled,\n );\n\n if (selectedIndex >= 0) {\n return selectedIndex;\n }\n\n return props.items.findIndex((item) => !item.disabled);\n};\n\nconst focusedIndex = ref(getInitialFocusedIndex());\n\nconst rootClasses = computed(() => [\n 't-nav-menu',\n `t-nav-menu--${props.size}`,\n {\n 'is-collapsed': resolvedCollapsed.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst isSelected = (value: string) => selectedValue.value === value;\n\nconst setItemRef = (element: Element | ComponentPublicInstance | null, value: string) => {\n const resolvedElement =\n element instanceof HTMLElement\n ? element\n : element && '$el' in element && element.$el instanceof HTMLElement\n ? element.$el\n : null;\n\n if (resolvedElement) {\n itemRefs.value.set(value, resolvedElement);\n } else {\n itemRefs.value.delete(value);\n }\n};\n\nconst focusItem = (index: number) => {\n const item = props.items[index];\n\n if (!item) {\n return;\n }\n\n focusedIndex.value = index;\n nextTick(() => itemRefs.value.get(item.value)?.focus());\n};\n\nconst selectItem = (item: TNavMenuItem) => {\n if (props.disabled || item.disabled) {\n return;\n }\n\n if (props.modelValue === undefined) {\n internalValue.value = item.value;\n }\n\n emit('update:modelValue', item.value);\n emit('select', item.value, item);\n};\n\nconst onItemKeydown = (event: KeyboardEvent, index: number) => {\n if (props.disabled) {\n return;\n }\n\n if (event.key === 'Home') {\n event.preventDefault();\n const firstIndex = props.items.findIndex((item) => !item.disabled);\n\n if (firstIndex >= 0) {\n focusItem(firstIndex);\n }\n\n return;\n }\n\n if (event.key === 'End') {\n event.preventDefault();\n const lastIndex = [...props.items].reverse().findIndex((item) => !item.disabled);\n\n if (lastIndex >= 0) {\n focusItem(props.items.length - 1 - lastIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n event.preventDefault();\n const nextIndex = getNextEnabledIndex(index, props.items, 1);\n\n if (nextIndex >= 0) {\n focusItem(nextIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n event.preventDefault();\n const previousIndex = getNextEnabledIndex(index, props.items, -1);\n\n if (previousIndex >= 0) {\n focusItem(previousIndex);\n }\n\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectItem(props.items[index]);\n }\n};\n\nconst getTabIndex = (index: number) => (focusedIndex.value === index ? 0 : -1);\n\nwatch(\n () => [props.items, props.modelValue] as const,\n () => {\n const nextIndex = getInitialFocusedIndex();\n focusedIndex.value = nextIndex >= 0 ? nextIndex : -1;\n },\n { deep: true },\n);\n</script>\n\n<template>\n <nav\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n >\n <ul\n v-if=\"items.length > 0\"\n class=\"t-nav-menu__list\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n class=\"t-nav-menu__entry\"\n >\n <component\n :is=\"itemTag(item)\"\n :ref=\"(element: Element | ComponentPublicInstance | null) => setItemRef(element, item.value)\"\n :type=\"item.to ? undefined : 'button'\"\n :to=\"item.to || undefined\"\n class=\"t-nav-menu__item\"\n :class=\"{\n 'is-selected': isSelected(item.value),\n 'is-disabled': disabled || item.disabled,\n }\"\n :disabled=\"(!item.to && (disabled || item.disabled)) || undefined\"\n :aria-disabled=\"(item.to && (disabled || item.disabled)) || undefined\"\n :aria-current=\"isSelected(item.value) ? 'page' : undefined\"\n :tabindex=\"disabled || item.disabled ? -1 : getTabIndex(index)\"\n :title=\"resolvedCollapsed ? item.label : undefined\"\n @click=\"selectItem(item)\"\n @keydown=\"onItemKeydown($event, index)\"\n @focus=\"focusedIndex = index\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :selected=\"isSelected(item.value)\"\n :focused=\"focusedIndex === index\"\n :collapsed=\"resolvedCollapsed\"\n >\n <component\n :is=\"item.icon\"\n v-if=\"item.icon\"\n class=\"t-nav-menu__icon\"\n aria-hidden=\"true\"\n />\n <span\n v-else\n class=\"t-nav-menu__marker\"\n aria-hidden=\"true\"\n >\n {{ (item.shortLabel ?? item.label.charAt(0)).toUpperCase() }}\n </span>\n\n <span class=\"t-nav-menu__copy\">\n <span class=\"t-nav-menu__label\">{{ item.label }}</span>\n <span\n v-if=\"item.description\"\n class=\"t-nav-menu__description\"\n >\n {{ item.description }}\n </span>\n </span>\n\n <span\n v-if=\"item.badge !== undefined && item.badge !== null\"\n class=\"t-nav-menu__badge\"\n >\n {{ item.badge }}\n </span>\n </slot>\n </component>\n </li>\n </ul>\n\n <div\n v-else\n class=\"t-nav-menu__empty\"\n >\n <slot name=\"empty\">\n No navigation items.\n </slot>\n </div>\n </nav>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"TNavMenu.cjs","names":[],"sources":["../../src/components/TNavMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { getNextEnabledIndex, isActivationKey } from '@treeui/utils';\nimport { computed, getCurrentInstance, inject, nextTick, ref, useAttrs, watch, type Component, type ComponentPublicInstance } from 'vue';\nimport type { TSize } from '../types/contracts';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport interface TNavMenuItem {\n label: string;\n value: string;\n shortLabel?: string;\n description?: string;\n icon?: Component;\n to?: string | Record<string, unknown>;\n badge?: string | number;\n disabled?: boolean;\n /**\n * When the item renders as a router link, controls whether an inclusive\n * (prefix) route match also marks it active. Defaults to the menu-level\n * `exact` prop. Set to `false` for section roots that should stay active on\n * nested routes.\n */\n exact?: boolean;\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n defaultValue?: string;\n items?: TNavMenuItem[];\n size?: TSize;\n collapsed?: boolean;\n disabled?: boolean;\n exact?: boolean;\n }>(),\n {\n modelValue: undefined,\n defaultValue: '',\n items: () => [],\n size: 'md',\n collapsed: undefined,\n disabled: false,\n exact: true,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n select: [value: string, item: TNavMenuItem];\n}>();\n\ndefineSlots<{\n item?: (props: {\n item: TNavMenuItem;\n selected: boolean;\n focused: boolean;\n collapsed: boolean;\n }) => unknown;\n empty?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebar = inject(treeSidebarInjectionKey, null);\nconst internalValue = ref(props.defaultValue);\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map());\nconst instance = getCurrentInstance();\n\nconst selectedValue = computed(() => props.modelValue ?? internalValue.value);\nconst resolvedCollapsed = computed(() => props.collapsed ?? sidebar?.collapsed.value ?? false);\n\nconst routerLinkComponent = computed<Component | null>(() => (\n (instance?.appContext.components.RouterLink as Component | undefined) ?? null\n));\n\nconst itemTag = (item: TNavMenuItem) => {\n if (item.to && routerLinkComponent.value) {\n return routerLinkComponent.value;\n }\n return 'button';\n};\n\nconst isRouterLinkItem = (item: TNavMenuItem) =>\n Boolean(item.to) && routerLinkComponent.value !== null;\n\n// Keep the active highlight deterministic when items render as router links.\n// Vue Router's default `router-link-active` uses inclusive (prefix) matching,\n// so several links can appear active at once. We map the router's active\n// classes onto TreeUI's own `is-selected`:\n// - Controlled menus (explicit `modelValue`) own selection, so the router adds\n// no class of its own — only the bound `is-selected` shows.\n// - Uncontrolled menus let the current route drive selection, using exact\n// matching by default (opt out per item, or menu-wide, via `exact`).\nconst routerActiveClass = (item: TNavMenuItem) => {\n if (!isRouterLinkItem(item)) {\n return undefined;\n }\n\n if (props.modelValue !== undefined) {\n return '';\n }\n\n return (item.exact ?? props.exact) ? '' : 'is-selected';\n};\n\nconst routerExactActiveClass = (item: TNavMenuItem) => {\n if (!isRouterLinkItem(item)) {\n return undefined;\n }\n\n return props.modelValue !== undefined ? '' : 'is-selected';\n};\n\nconst getInitialFocusedIndex = () => {\n const selectedIndex = props.items.findIndex((item) =>\n item.value === selectedValue.value && !item.disabled,\n );\n\n if (selectedIndex >= 0) {\n return selectedIndex;\n }\n\n return props.items.findIndex((item) => !item.disabled);\n};\n\nconst focusedIndex = ref(getInitialFocusedIndex());\n\nconst rootClasses = computed(() => [\n 't-nav-menu',\n `t-nav-menu--${props.size}`,\n {\n 'is-collapsed': resolvedCollapsed.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst isSelected = (value: string) => selectedValue.value === value;\n\nconst setItemRef = (element: Element | ComponentPublicInstance | null, value: string) => {\n const resolvedElement =\n element instanceof HTMLElement\n ? element\n : element && '$el' in element && element.$el instanceof HTMLElement\n ? element.$el\n : null;\n\n if (resolvedElement) {\n itemRefs.value.set(value, resolvedElement);\n } else {\n itemRefs.value.delete(value);\n }\n};\n\nconst focusItem = (index: number) => {\n const item = props.items[index];\n\n if (!item) {\n return;\n }\n\n focusedIndex.value = index;\n nextTick(() => itemRefs.value.get(item.value)?.focus());\n};\n\nconst selectItem = (item: TNavMenuItem) => {\n if (props.disabled || item.disabled) {\n return;\n }\n\n if (props.modelValue === undefined) {\n internalValue.value = item.value;\n }\n\n emit('update:modelValue', item.value);\n emit('select', item.value, item);\n};\n\nconst onItemKeydown = (event: KeyboardEvent, index: number) => {\n if (props.disabled) {\n return;\n }\n\n if (event.key === 'Home') {\n event.preventDefault();\n const firstIndex = props.items.findIndex((item) => !item.disabled);\n\n if (firstIndex >= 0) {\n focusItem(firstIndex);\n }\n\n return;\n }\n\n if (event.key === 'End') {\n event.preventDefault();\n const lastIndex = [...props.items].reverse().findIndex((item) => !item.disabled);\n\n if (lastIndex >= 0) {\n focusItem(props.items.length - 1 - lastIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n event.preventDefault();\n const nextIndex = getNextEnabledIndex(index, props.items, 1);\n\n if (nextIndex >= 0) {\n focusItem(nextIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n event.preventDefault();\n const previousIndex = getNextEnabledIndex(index, props.items, -1);\n\n if (previousIndex >= 0) {\n focusItem(previousIndex);\n }\n\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectItem(props.items[index]);\n }\n};\n\nconst getTabIndex = (index: number) => (focusedIndex.value === index ? 0 : -1);\n\nwatch(\n () => [props.items, props.modelValue] as const,\n () => {\n const nextIndex = getInitialFocusedIndex();\n focusedIndex.value = nextIndex >= 0 ? nextIndex : -1;\n },\n { deep: true },\n);\n</script>\n\n<template>\n <nav\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n >\n <ul\n v-if=\"items.length > 0\"\n class=\"t-nav-menu__list\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n class=\"t-nav-menu__entry\"\n >\n <component\n :is=\"itemTag(item)\"\n :ref=\"(element: Element | ComponentPublicInstance | null) => setItemRef(element, item.value)\"\n :type=\"item.to ? undefined : 'button'\"\n :to=\"item.to || undefined\"\n class=\"t-nav-menu__item\"\n :class=\"{\n 'is-selected': isSelected(item.value),\n 'is-disabled': disabled || item.disabled,\n }\"\n :active-class=\"routerActiveClass(item)\"\n :exact-active-class=\"routerExactActiveClass(item)\"\n :disabled=\"(!item.to && (disabled || item.disabled)) || undefined\"\n :aria-disabled=\"(item.to && (disabled || item.disabled)) || undefined\"\n :aria-current=\"isSelected(item.value) ? 'page' : undefined\"\n :tabindex=\"disabled || item.disabled ? -1 : getTabIndex(index)\"\n :title=\"resolvedCollapsed ? item.label : undefined\"\n @click=\"selectItem(item)\"\n @keydown=\"onItemKeydown($event, index)\"\n @focus=\"focusedIndex = index\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :selected=\"isSelected(item.value)\"\n :focused=\"focusedIndex === index\"\n :collapsed=\"resolvedCollapsed\"\n >\n <component\n :is=\"item.icon\"\n v-if=\"item.icon\"\n class=\"t-nav-menu__icon\"\n aria-hidden=\"true\"\n />\n <span\n v-else\n class=\"t-nav-menu__marker\"\n aria-hidden=\"true\"\n >\n {{ (item.shortLabel ?? item.label.charAt(0)).toUpperCase() }}\n </span>\n\n <span class=\"t-nav-menu__copy\">\n <span class=\"t-nav-menu__label\">{{ item.label }}</span>\n <span\n v-if=\"item.description\"\n class=\"t-nav-menu__description\"\n >\n {{ item.description }}\n </span>\n </span>\n\n <span\n v-if=\"item.badge !== undefined && item.badge !== null\"\n class=\"t-nav-menu__badge\"\n >\n {{ item.badge }}\n </span>\n </slot>\n </component>\n </li>\n </ul>\n\n <div\n v-else\n class=\"t-nav-menu__empty\"\n >\n <slot name=\"empty\">\n No navigation items.\n </slot>\n </div>\n </nav>\n</template>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"TNavMenu.js","names":[],"sources":["../../src/components/TNavMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { getNextEnabledIndex, isActivationKey } from '@treeui/utils';\nimport { computed, getCurrentInstance, inject, nextTick, ref, useAttrs, watch, type Component, type ComponentPublicInstance } from 'vue';\nimport type { TSize } from '../types/contracts';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport interface TNavMenuItem {\n label: string;\n value: string;\n shortLabel?: string;\n description?: string;\n icon?: Component;\n to?: string | Record<string, unknown>;\n badge?: string | number;\n disabled?: boolean;\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n defaultValue?: string;\n items?: TNavMenuItem[];\n size?: TSize;\n collapsed?: boolean;\n disabled?: boolean;\n }>(),\n {\n modelValue: undefined,\n defaultValue: '',\n items: () => [],\n size: 'md',\n collapsed: undefined,\n disabled: false,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n select: [value: string, item: TNavMenuItem];\n}>();\n\ndefineSlots<{\n item?: (props: {\n item: TNavMenuItem;\n selected: boolean;\n focused: boolean;\n collapsed: boolean;\n }) => unknown;\n empty?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebar = inject(treeSidebarInjectionKey, null);\nconst internalValue = ref(props.defaultValue);\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map());\nconst instance = getCurrentInstance();\n\nconst selectedValue = computed(() => props.modelValue ?? internalValue.value);\nconst resolvedCollapsed = computed(() => props.collapsed ?? sidebar?.collapsed.value ?? false);\n\nconst routerLinkComponent = computed<Component | null>(() => (\n (instance?.appContext.components.RouterLink as Component | undefined) ?? null\n));\n\nconst itemTag = (item: TNavMenuItem) => {\n if (item.to && routerLinkComponent.value) {\n return routerLinkComponent.value;\n }\n return 'button';\n};\n\nconst getInitialFocusedIndex = () => {\n const selectedIndex = props.items.findIndex((item) =>\n item.value === selectedValue.value && !item.disabled,\n );\n\n if (selectedIndex >= 0) {\n return selectedIndex;\n }\n\n return props.items.findIndex((item) => !item.disabled);\n};\n\nconst focusedIndex = ref(getInitialFocusedIndex());\n\nconst rootClasses = computed(() => [\n 't-nav-menu',\n `t-nav-menu--${props.size}`,\n {\n 'is-collapsed': resolvedCollapsed.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst isSelected = (value: string) => selectedValue.value === value;\n\nconst setItemRef = (element: Element | ComponentPublicInstance | null, value: string) => {\n const resolvedElement =\n element instanceof HTMLElement\n ? element\n : element && '$el' in element && element.$el instanceof HTMLElement\n ? element.$el\n : null;\n\n if (resolvedElement) {\n itemRefs.value.set(value, resolvedElement);\n } else {\n itemRefs.value.delete(value);\n }\n};\n\nconst focusItem = (index: number) => {\n const item = props.items[index];\n\n if (!item) {\n return;\n }\n\n focusedIndex.value = index;\n nextTick(() => itemRefs.value.get(item.value)?.focus());\n};\n\nconst selectItem = (item: TNavMenuItem) => {\n if (props.disabled || item.disabled) {\n return;\n }\n\n if (props.modelValue === undefined) {\n internalValue.value = item.value;\n }\n\n emit('update:modelValue', item.value);\n emit('select', item.value, item);\n};\n\nconst onItemKeydown = (event: KeyboardEvent, index: number) => {\n if (props.disabled) {\n return;\n }\n\n if (event.key === 'Home') {\n event.preventDefault();\n const firstIndex = props.items.findIndex((item) => !item.disabled);\n\n if (firstIndex >= 0) {\n focusItem(firstIndex);\n }\n\n return;\n }\n\n if (event.key === 'End') {\n event.preventDefault();\n const lastIndex = [...props.items].reverse().findIndex((item) => !item.disabled);\n\n if (lastIndex >= 0) {\n focusItem(props.items.length - 1 - lastIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n event.preventDefault();\n const nextIndex = getNextEnabledIndex(index, props.items, 1);\n\n if (nextIndex >= 0) {\n focusItem(nextIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n event.preventDefault();\n const previousIndex = getNextEnabledIndex(index, props.items, -1);\n\n if (previousIndex >= 0) {\n focusItem(previousIndex);\n }\n\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectItem(props.items[index]);\n }\n};\n\nconst getTabIndex = (index: number) => (focusedIndex.value === index ? 0 : -1);\n\nwatch(\n () => [props.items, props.modelValue] as const,\n () => {\n const nextIndex = getInitialFocusedIndex();\n focusedIndex.value = nextIndex >= 0 ? nextIndex : -1;\n },\n { deep: true },\n);\n</script>\n\n<template>\n <nav\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n >\n <ul\n v-if=\"items.length > 0\"\n class=\"t-nav-menu__list\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n class=\"t-nav-menu__entry\"\n >\n <component\n :is=\"itemTag(item)\"\n :ref=\"(element: Element | ComponentPublicInstance | null) => setItemRef(element, item.value)\"\n :type=\"item.to ? undefined : 'button'\"\n :to=\"item.to || undefined\"\n class=\"t-nav-menu__item\"\n :class=\"{\n 'is-selected': isSelected(item.value),\n 'is-disabled': disabled || item.disabled,\n }\"\n :disabled=\"(!item.to && (disabled || item.disabled)) || undefined\"\n :aria-disabled=\"(item.to && (disabled || item.disabled)) || undefined\"\n :aria-current=\"isSelected(item.value) ? 'page' : undefined\"\n :tabindex=\"disabled || item.disabled ? -1 : getTabIndex(index)\"\n :title=\"resolvedCollapsed ? item.label : undefined\"\n @click=\"selectItem(item)\"\n @keydown=\"onItemKeydown($event, index)\"\n @focus=\"focusedIndex = index\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :selected=\"isSelected(item.value)\"\n :focused=\"focusedIndex === index\"\n :collapsed=\"resolvedCollapsed\"\n >\n <component\n :is=\"item.icon\"\n v-if=\"item.icon\"\n class=\"t-nav-menu__icon\"\n aria-hidden=\"true\"\n />\n <span\n v-else\n class=\"t-nav-menu__marker\"\n aria-hidden=\"true\"\n >\n {{ (item.shortLabel ?? item.label.charAt(0)).toUpperCase() }}\n </span>\n\n <span class=\"t-nav-menu__copy\">\n <span class=\"t-nav-menu__label\">{{ item.label }}</span>\n <span\n v-if=\"item.description\"\n class=\"t-nav-menu__description\"\n >\n {{ item.description }}\n </span>\n </span>\n\n <span\n v-if=\"item.badge !== undefined && item.badge !== null\"\n class=\"t-nav-menu__badge\"\n >\n {{ item.badge }}\n </span>\n </slot>\n </component>\n </li>\n </ul>\n\n <div\n v-else\n class=\"t-nav-menu__empty\"\n >\n <slot name=\"empty\">\n No navigation items.\n </slot>\n </div>\n </nav>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"TNavMenu.js","names":[],"sources":["../../src/components/TNavMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { getNextEnabledIndex, isActivationKey } from '@treeui/utils';\nimport { computed, getCurrentInstance, inject, nextTick, ref, useAttrs, watch, type Component, type ComponentPublicInstance } from 'vue';\nimport type { TSize } from '../types/contracts';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport interface TNavMenuItem {\n label: string;\n value: string;\n shortLabel?: string;\n description?: string;\n icon?: Component;\n to?: string | Record<string, unknown>;\n badge?: string | number;\n disabled?: boolean;\n /**\n * When the item renders as a router link, controls whether an inclusive\n * (prefix) route match also marks it active. Defaults to the menu-level\n * `exact` prop. Set to `false` for section roots that should stay active on\n * nested routes.\n */\n exact?: boolean;\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n defaultValue?: string;\n items?: TNavMenuItem[];\n size?: TSize;\n collapsed?: boolean;\n disabled?: boolean;\n exact?: boolean;\n }>(),\n {\n modelValue: undefined,\n defaultValue: '',\n items: () => [],\n size: 'md',\n collapsed: undefined,\n disabled: false,\n exact: true,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n select: [value: string, item: TNavMenuItem];\n}>();\n\ndefineSlots<{\n item?: (props: {\n item: TNavMenuItem;\n selected: boolean;\n focused: boolean;\n collapsed: boolean;\n }) => unknown;\n empty?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebar = inject(treeSidebarInjectionKey, null);\nconst internalValue = ref(props.defaultValue);\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map());\nconst instance = getCurrentInstance();\n\nconst selectedValue = computed(() => props.modelValue ?? internalValue.value);\nconst resolvedCollapsed = computed(() => props.collapsed ?? sidebar?.collapsed.value ?? false);\n\nconst routerLinkComponent = computed<Component | null>(() => (\n (instance?.appContext.components.RouterLink as Component | undefined) ?? null\n));\n\nconst itemTag = (item: TNavMenuItem) => {\n if (item.to && routerLinkComponent.value) {\n return routerLinkComponent.value;\n }\n return 'button';\n};\n\nconst isRouterLinkItem = (item: TNavMenuItem) =>\n Boolean(item.to) && routerLinkComponent.value !== null;\n\n// Keep the active highlight deterministic when items render as router links.\n// Vue Router's default `router-link-active` uses inclusive (prefix) matching,\n// so several links can appear active at once. We map the router's active\n// classes onto TreeUI's own `is-selected`:\n// - Controlled menus (explicit `modelValue`) own selection, so the router adds\n// no class of its own — only the bound `is-selected` shows.\n// - Uncontrolled menus let the current route drive selection, using exact\n// matching by default (opt out per item, or menu-wide, via `exact`).\nconst routerActiveClass = (item: TNavMenuItem) => {\n if (!isRouterLinkItem(item)) {\n return undefined;\n }\n\n if (props.modelValue !== undefined) {\n return '';\n }\n\n return (item.exact ?? props.exact) ? '' : 'is-selected';\n};\n\nconst routerExactActiveClass = (item: TNavMenuItem) => {\n if (!isRouterLinkItem(item)) {\n return undefined;\n }\n\n return props.modelValue !== undefined ? '' : 'is-selected';\n};\n\nconst getInitialFocusedIndex = () => {\n const selectedIndex = props.items.findIndex((item) =>\n item.value === selectedValue.value && !item.disabled,\n );\n\n if (selectedIndex >= 0) {\n return selectedIndex;\n }\n\n return props.items.findIndex((item) => !item.disabled);\n};\n\nconst focusedIndex = ref(getInitialFocusedIndex());\n\nconst rootClasses = computed(() => [\n 't-nav-menu',\n `t-nav-menu--${props.size}`,\n {\n 'is-collapsed': resolvedCollapsed.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst isSelected = (value: string) => selectedValue.value === value;\n\nconst setItemRef = (element: Element | ComponentPublicInstance | null, value: string) => {\n const resolvedElement =\n element instanceof HTMLElement\n ? element\n : element && '$el' in element && element.$el instanceof HTMLElement\n ? element.$el\n : null;\n\n if (resolvedElement) {\n itemRefs.value.set(value, resolvedElement);\n } else {\n itemRefs.value.delete(value);\n }\n};\n\nconst focusItem = (index: number) => {\n const item = props.items[index];\n\n if (!item) {\n return;\n }\n\n focusedIndex.value = index;\n nextTick(() => itemRefs.value.get(item.value)?.focus());\n};\n\nconst selectItem = (item: TNavMenuItem) => {\n if (props.disabled || item.disabled) {\n return;\n }\n\n if (props.modelValue === undefined) {\n internalValue.value = item.value;\n }\n\n emit('update:modelValue', item.value);\n emit('select', item.value, item);\n};\n\nconst onItemKeydown = (event: KeyboardEvent, index: number) => {\n if (props.disabled) {\n return;\n }\n\n if (event.key === 'Home') {\n event.preventDefault();\n const firstIndex = props.items.findIndex((item) => !item.disabled);\n\n if (firstIndex >= 0) {\n focusItem(firstIndex);\n }\n\n return;\n }\n\n if (event.key === 'End') {\n event.preventDefault();\n const lastIndex = [...props.items].reverse().findIndex((item) => !item.disabled);\n\n if (lastIndex >= 0) {\n focusItem(props.items.length - 1 - lastIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n event.preventDefault();\n const nextIndex = getNextEnabledIndex(index, props.items, 1);\n\n if (nextIndex >= 0) {\n focusItem(nextIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n event.preventDefault();\n const previousIndex = getNextEnabledIndex(index, props.items, -1);\n\n if (previousIndex >= 0) {\n focusItem(previousIndex);\n }\n\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectItem(props.items[index]);\n }\n};\n\nconst getTabIndex = (index: number) => (focusedIndex.value === index ? 0 : -1);\n\nwatch(\n () => [props.items, props.modelValue] as const,\n () => {\n const nextIndex = getInitialFocusedIndex();\n focusedIndex.value = nextIndex >= 0 ? nextIndex : -1;\n },\n { deep: true },\n);\n</script>\n\n<template>\n <nav\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n >\n <ul\n v-if=\"items.length > 0\"\n class=\"t-nav-menu__list\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n class=\"t-nav-menu__entry\"\n >\n <component\n :is=\"itemTag(item)\"\n :ref=\"(element: Element | ComponentPublicInstance | null) => setItemRef(element, item.value)\"\n :type=\"item.to ? undefined : 'button'\"\n :to=\"item.to || undefined\"\n class=\"t-nav-menu__item\"\n :class=\"{\n 'is-selected': isSelected(item.value),\n 'is-disabled': disabled || item.disabled,\n }\"\n :active-class=\"routerActiveClass(item)\"\n :exact-active-class=\"routerExactActiveClass(item)\"\n :disabled=\"(!item.to && (disabled || item.disabled)) || undefined\"\n :aria-disabled=\"(item.to && (disabled || item.disabled)) || undefined\"\n :aria-current=\"isSelected(item.value) ? 'page' : undefined\"\n :tabindex=\"disabled || item.disabled ? -1 : getTabIndex(index)\"\n :title=\"resolvedCollapsed ? item.label : undefined\"\n @click=\"selectItem(item)\"\n @keydown=\"onItemKeydown($event, index)\"\n @focus=\"focusedIndex = index\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :selected=\"isSelected(item.value)\"\n :focused=\"focusedIndex === index\"\n :collapsed=\"resolvedCollapsed\"\n >\n <component\n :is=\"item.icon\"\n v-if=\"item.icon\"\n class=\"t-nav-menu__icon\"\n aria-hidden=\"true\"\n />\n <span\n v-else\n class=\"t-nav-menu__marker\"\n aria-hidden=\"true\"\n >\n {{ (item.shortLabel ?? item.label.charAt(0)).toUpperCase() }}\n </span>\n\n <span class=\"t-nav-menu__copy\">\n <span class=\"t-nav-menu__label\">{{ item.label }}</span>\n <span\n v-if=\"item.description\"\n class=\"t-nav-menu__description\"\n >\n {{ item.description }}\n </span>\n </span>\n\n <span\n v-if=\"item.badge !== undefined && item.badge !== null\"\n class=\"t-nav-menu__badge\"\n >\n {{ item.badge }}\n </span>\n </slot>\n </component>\n </li>\n </ul>\n\n <div\n v-else\n class=\"t-nav-menu__empty\"\n >\n <slot name=\"empty\">\n No navigation items.\n </slot>\n </div>\n </nav>\n</template>\n"],"mappings":""}
@@ -9,6 +9,13 @@ export interface TNavMenuItem {
9
9
  to?: string | Record<string, unknown>;
10
10
  badge?: string | number;
11
11
  disabled?: boolean;
12
+ /**
13
+ * When the item renders as a router link, controls whether an inclusive
14
+ * (prefix) route match also marks it active. Defaults to the menu-level
15
+ * `exact` prop. Set to `false` for section roots that should stay active on
16
+ * nested routes.
17
+ */
18
+ exact?: boolean;
12
19
  }
13
20
  type __VLS_Props = {
14
21
  modelValue?: string;
@@ -17,6 +24,7 @@ type __VLS_Props = {
17
24
  size?: TSize;
18
25
  collapsed?: boolean;
19
26
  disabled?: boolean;
27
+ exact?: boolean;
20
28
  };
21
29
  declare function __VLS_template(): {
22
30
  attrs: Partial<{}>;
@@ -53,6 +61,7 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}
53
61
  disabled: boolean;
54
62
  size: TSize;
55
63
  items: TNavMenuItem[];
64
+ exact: boolean;
56
65
  collapsed: boolean;
57
66
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
58
67
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
@@ -1 +1 @@
1
- {"version":3,"file":"TNavMenu.vue.d.ts","sourceRoot":"","sources":["../../src/components/TNavMenu.vue"],"names":[],"mappings":"AA+SA,OAAO,EAAwE,KAAK,SAAS,EAAgC,MAAM,KAAK,CAAC;AACzI,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAOhD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,KAAK,WAAW,GAAG;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAqMJ,iBAAS,cAAc;WA8IT,OAAO,IAA6B;;eA/TzC,CAAC,KAAK,EAAE;YACb,IAAI,EAAE,YAAY,CAAC;YACnB,QAAQ,EAAE,OAAO,CAAC;YAClB,OAAO,EAAE,OAAO,CAAC;YACjB,SAAS,EAAE,OAAO,CAAC;SACpB,KAAK,OAAO;gBACL,MAAM,OAAO;;eANd,CAAC,KAAK,EAAE;YACb,IAAI,EAAE,YAAY,CAAC;YACnB,QAAQ,EAAE,OAAO,CAAC;YAClB,OAAO,EAAE,OAAO,CAAC;YACjB,SAAS,EAAE,OAAO,CAAC;SACpB,KAAK,OAAO;gBACL,MAAM,OAAO;;;;EA8TtB;AAqBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;gBApXJ,MAAM;kBACJ,MAAM;cAIV,OAAO;UAFX,KAAK;WADJ,YAAY,EAAE;eAEV,OAAO;6EAwXrB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"TNavMenu.vue.d.ts","sourceRoot":"","sources":["../../src/components/TNavMenu.vue"],"names":[],"mappings":"AAyVA,OAAO,EAAwE,KAAK,SAAS,EAAgC,MAAM,KAAK,CAAC;AACzI,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAOhD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,KAAK,WAAW,GAAG;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAsOJ,iBAAS,cAAc;WAkJT,OAAO,IAA6B;;eAnWzC,CAAC,KAAK,EAAE;YACb,IAAI,EAAE,YAAY,CAAC;YACnB,QAAQ,EAAE,OAAO,CAAC;YAClB,OAAO,EAAE,OAAO,CAAC;YACjB,SAAS,EAAE,OAAO,CAAC;SACpB,KAAK,OAAO;gBACL,MAAM,OAAO;;eANd,CAAC,KAAK,EAAE;YACb,IAAI,EAAE,YAAY,CAAC;YACnB,QAAQ,EAAE,OAAO,CAAC;YAClB,OAAO,EAAE,OAAO,CAAC;YACjB,SAAS,EAAE,OAAO,CAAC;SACpB,KAAK,OAAO;gBACL,MAAM,OAAO;;;;EAkWtB;AAuBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;gBA5ZJ,MAAM;kBACJ,MAAM;cAIV,OAAO;UAFX,KAAK;WADJ,YAAY,EAAE;WAId,OAAO;eAFH,OAAO;6EAgarB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -1,2 +1,2 @@
1
- const e=require("./sidebar.cjs");let t=require("vue"),n=require("@treeui/utils");var r={key:0,class:`t-nav-menu__list`},i={key:1,class:`t-nav-menu__marker`,"aria-hidden":`true`},a={class:`t-nav-menu__copy`},o={class:`t-nav-menu__label`},s={key:0,class:`t-nav-menu__description`},c={key:2,class:`t-nav-menu__badge`},l={key:1,class:`t-nav-menu__empty`},u=(0,t.defineComponent)({inheritAttrs:!1,__name:`TNavMenu`,props:{modelValue:{default:void 0},defaultValue:{default:``},items:{default:()=>[]},size:{default:`md`},collapsed:{type:Boolean,default:void 0},disabled:{type:Boolean,default:!1}},emits:[`update:modelValue`,`select`],setup(u,{emit:d}){let f=u,p=d,m=(0,t.useAttrs)(),h=(0,t.inject)(e.treeSidebarInjectionKey,null),g=(0,t.ref)(f.defaultValue),_=(0,t.ref)(new Map),v=(0,t.getCurrentInstance)(),y=(0,t.computed)(()=>f.modelValue??g.value),b=(0,t.computed)(()=>f.collapsed??h?.collapsed.value??!1),x=(0,t.computed)(()=>v?.appContext.components.RouterLink??null),S=e=>e.to&&x.value?x.value:`button`,C=()=>{let e=f.items.findIndex(e=>e.value===y.value&&!e.disabled);return e>=0?e:f.items.findIndex(e=>!e.disabled)},w=(0,t.ref)(C()),T=(0,t.computed)(()=>[`t-nav-menu`,`t-nav-menu--${f.size}`,{"is-collapsed":b.value,"is-disabled":f.disabled},m.class]),E=(0,t.computed)(()=>m.style),D=(0,t.computed)(()=>{let{class:e,style:t,...n}=m;return n}),O=e=>y.value===e,k=(e,t)=>{let n=e instanceof HTMLElement?e:e&&`$el`in e&&e.$el instanceof HTMLElement?e.$el:null;n?_.value.set(t,n):_.value.delete(t)},A=e=>{let n=f.items[e];n&&(w.value=e,(0,t.nextTick)(()=>_.value.get(n.value)?.focus()))},j=e=>{f.disabled||e.disabled||(f.modelValue===void 0&&(g.value=e.value),p(`update:modelValue`,e.value),p(`select`,e.value,e))},M=(e,t)=>{if(!f.disabled){if(e.key===`Home`){e.preventDefault();let t=f.items.findIndex(e=>!e.disabled);t>=0&&A(t);return}if(e.key===`End`){e.preventDefault();let t=[...f.items].reverse().findIndex(e=>!e.disabled);t>=0&&A(f.items.length-1-t);return}if(e.key===`ArrowDown`||e.key===`ArrowRight`){e.preventDefault();let r=(0,n.getNextEnabledIndex)(t,f.items,1);r>=0&&A(r);return}if(e.key===`ArrowUp`||e.key===`ArrowLeft`){e.preventDefault();let r=(0,n.getNextEnabledIndex)(t,f.items,-1);r>=0&&A(r);return}(0,n.isActivationKey)(e)&&(e.preventDefault(),j(f.items[t]))}},N=e=>w.value===e?0:-1;return(0,t.watch)(()=>[f.items,f.modelValue],()=>{let e=C();w.value=e>=0?e:-1},{deep:!0}),(e,n)=>((0,t.openBlock)(),(0,t.createElementBlock)(`nav`,(0,t.mergeProps)(D.value,{class:T.value,style:E.value}),[u.items.length>0?((0,t.openBlock)(),(0,t.createElementBlock)(`ul`,r,[((0,t.openBlock)(!0),(0,t.createElementBlock)(t.Fragment,null,(0,t.renderList)(u.items,(n,r)=>((0,t.openBlock)(),(0,t.createElementBlock)(`li`,{key:n.value,class:`t-nav-menu__entry`},[((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(S(n)),{ref_for:!0,ref:e=>k(e,n.value),type:n.to?void 0:`button`,to:n.to||void 0,class:(0,t.normalizeClass)([`t-nav-menu__item`,{"is-selected":O(n.value),"is-disabled":u.disabled||n.disabled}]),disabled:!n.to&&(u.disabled||n.disabled)||void 0,"aria-disabled":n.to&&(u.disabled||n.disabled)||void 0,"aria-current":O(n.value)?`page`:void 0,tabindex:u.disabled||n.disabled?-1:N(r),title:b.value?n.label:void 0,onClick:e=>j(n),onKeydown:e=>M(e,r),onFocus:e=>w.value=r},{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`item`,{item:n,selected:O(n.value),focused:w.value===r,collapsed:b.value},()=>[n.icon?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(n.icon),{key:0,class:`t-nav-menu__icon`,"aria-hidden":`true`})):((0,t.openBlock)(),(0,t.createElementBlock)(`span`,i,(0,t.toDisplayString)((n.shortLabel??n.label.charAt(0)).toUpperCase()),1)),(0,t.createElementVNode)(`span`,a,[(0,t.createElementVNode)(`span`,o,(0,t.toDisplayString)(n.label),1),n.description?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,s,(0,t.toDisplayString)(n.description),1)):(0,t.createCommentVNode)(``,!0)]),n.badge!==void 0&&n.badge!==null?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,c,(0,t.toDisplayString)(n.badge),1)):(0,t.createCommentVNode)(``,!0)])]),_:2},1064,[`type`,`to`,`class`,`disabled`,`aria-disabled`,`aria-current`,`tabindex`,`title`,`onClick`,`onKeydown`,`onFocus`]))]))),128))])):((0,t.openBlock)(),(0,t.createElementBlock)(`div`,l,[(0,t.renderSlot)(e.$slots,`empty`,{},()=>[n[0]||=(0,t.createTextVNode)(` No navigation items. `,-1)])]))],16))}});exports.default=u;
1
+ const e=require("./sidebar.cjs");let t=require("vue"),n=require("@treeui/utils");var r={key:0,class:`t-nav-menu__list`},i={key:1,class:`t-nav-menu__marker`,"aria-hidden":`true`},a={class:`t-nav-menu__copy`},o={class:`t-nav-menu__label`},s={key:0,class:`t-nav-menu__description`},c={key:2,class:`t-nav-menu__badge`},l={key:1,class:`t-nav-menu__empty`},u=(0,t.defineComponent)({inheritAttrs:!1,__name:`TNavMenu`,props:{modelValue:{default:void 0},defaultValue:{default:``},items:{default:()=>[]},size:{default:`md`},collapsed:{type:Boolean,default:void 0},disabled:{type:Boolean,default:!1},exact:{type:Boolean,default:!0}},emits:[`update:modelValue`,`select`],setup(u,{emit:d}){let f=u,p=d,m=(0,t.useAttrs)(),h=(0,t.inject)(e.treeSidebarInjectionKey,null),g=(0,t.ref)(f.defaultValue),_=(0,t.ref)(new Map),v=(0,t.getCurrentInstance)(),y=(0,t.computed)(()=>f.modelValue??g.value),b=(0,t.computed)(()=>f.collapsed??h?.collapsed.value??!1),x=(0,t.computed)(()=>v?.appContext.components.RouterLink??null),S=e=>e.to&&x.value?x.value:`button`,C=e=>!!e.to&&x.value!==null,w=e=>{if(C(e))return f.modelValue===void 0?e.exact??f.exact?``:`is-selected`:``},T=e=>{if(C(e))return f.modelValue===void 0?`is-selected`:``},E=()=>{let e=f.items.findIndex(e=>e.value===y.value&&!e.disabled);return e>=0?e:f.items.findIndex(e=>!e.disabled)},D=(0,t.ref)(E()),O=(0,t.computed)(()=>[`t-nav-menu`,`t-nav-menu--${f.size}`,{"is-collapsed":b.value,"is-disabled":f.disabled},m.class]),k=(0,t.computed)(()=>m.style),A=(0,t.computed)(()=>{let{class:e,style:t,...n}=m;return n}),j=e=>y.value===e,M=(e,t)=>{let n=e instanceof HTMLElement?e:e&&`$el`in e&&e.$el instanceof HTMLElement?e.$el:null;n?_.value.set(t,n):_.value.delete(t)},N=e=>{let n=f.items[e];n&&(D.value=e,(0,t.nextTick)(()=>_.value.get(n.value)?.focus()))},P=e=>{f.disabled||e.disabled||(f.modelValue===void 0&&(g.value=e.value),p(`update:modelValue`,e.value),p(`select`,e.value,e))},F=(e,t)=>{if(!f.disabled){if(e.key===`Home`){e.preventDefault();let t=f.items.findIndex(e=>!e.disabled);t>=0&&N(t);return}if(e.key===`End`){e.preventDefault();let t=[...f.items].reverse().findIndex(e=>!e.disabled);t>=0&&N(f.items.length-1-t);return}if(e.key===`ArrowDown`||e.key===`ArrowRight`){e.preventDefault();let r=(0,n.getNextEnabledIndex)(t,f.items,1);r>=0&&N(r);return}if(e.key===`ArrowUp`||e.key===`ArrowLeft`){e.preventDefault();let r=(0,n.getNextEnabledIndex)(t,f.items,-1);r>=0&&N(r);return}(0,n.isActivationKey)(e)&&(e.preventDefault(),P(f.items[t]))}},I=e=>D.value===e?0:-1;return(0,t.watch)(()=>[f.items,f.modelValue],()=>{let e=E();D.value=e>=0?e:-1},{deep:!0}),(e,n)=>((0,t.openBlock)(),(0,t.createElementBlock)(`nav`,(0,t.mergeProps)(A.value,{class:O.value,style:k.value}),[u.items.length>0?((0,t.openBlock)(),(0,t.createElementBlock)(`ul`,r,[((0,t.openBlock)(!0),(0,t.createElementBlock)(t.Fragment,null,(0,t.renderList)(u.items,(n,r)=>((0,t.openBlock)(),(0,t.createElementBlock)(`li`,{key:n.value,class:`t-nav-menu__entry`},[((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(S(n)),{ref_for:!0,ref:e=>M(e,n.value),type:n.to?void 0:`button`,to:n.to||void 0,class:(0,t.normalizeClass)([`t-nav-menu__item`,{"is-selected":j(n.value),"is-disabled":u.disabled||n.disabled}]),"active-class":w(n),"exact-active-class":T(n),disabled:!n.to&&(u.disabled||n.disabled)||void 0,"aria-disabled":n.to&&(u.disabled||n.disabled)||void 0,"aria-current":j(n.value)?`page`:void 0,tabindex:u.disabled||n.disabled?-1:I(r),title:b.value?n.label:void 0,onClick:e=>P(n),onKeydown:e=>F(e,r),onFocus:e=>D.value=r},{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`item`,{item:n,selected:j(n.value),focused:D.value===r,collapsed:b.value},()=>[n.icon?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(n.icon),{key:0,class:`t-nav-menu__icon`,"aria-hidden":`true`})):((0,t.openBlock)(),(0,t.createElementBlock)(`span`,i,(0,t.toDisplayString)((n.shortLabel??n.label.charAt(0)).toUpperCase()),1)),(0,t.createElementVNode)(`span`,a,[(0,t.createElementVNode)(`span`,o,(0,t.toDisplayString)(n.label),1),n.description?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,s,(0,t.toDisplayString)(n.description),1)):(0,t.createCommentVNode)(``,!0)]),n.badge!==void 0&&n.badge!==null?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,c,(0,t.toDisplayString)(n.badge),1)):(0,t.createCommentVNode)(``,!0)])]),_:2},1064,[`type`,`to`,`class`,`active-class`,`exact-active-class`,`disabled`,`aria-disabled`,`aria-current`,`tabindex`,`title`,`onClick`,`onKeydown`,`onFocus`]))]))),128))])):((0,t.openBlock)(),(0,t.createElementBlock)(`div`,l,[(0,t.renderSlot)(e.$slots,`empty`,{},()=>[n[0]||=(0,t.createTextVNode)(` No navigation items. `,-1)])]))],16))}});exports.default=u;
2
2
  //# sourceMappingURL=TNavMenu.vue_vue_type_script_setup_true_lang.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"TNavMenu.vue_vue_type_script_setup_true_lang.cjs","names":[],"sources":["../../src/components/TNavMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { getNextEnabledIndex, isActivationKey } from '@treeui/utils';\nimport { computed, getCurrentInstance, inject, nextTick, ref, useAttrs, watch, type Component, type ComponentPublicInstance } from 'vue';\nimport type { TSize } from '../types/contracts';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport interface TNavMenuItem {\n label: string;\n value: string;\n shortLabel?: string;\n description?: string;\n icon?: Component;\n to?: string | Record<string, unknown>;\n badge?: string | number;\n disabled?: boolean;\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n defaultValue?: string;\n items?: TNavMenuItem[];\n size?: TSize;\n collapsed?: boolean;\n disabled?: boolean;\n }>(),\n {\n modelValue: undefined,\n defaultValue: '',\n items: () => [],\n size: 'md',\n collapsed: undefined,\n disabled: false,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n select: [value: string, item: TNavMenuItem];\n}>();\n\ndefineSlots<{\n item?: (props: {\n item: TNavMenuItem;\n selected: boolean;\n focused: boolean;\n collapsed: boolean;\n }) => unknown;\n empty?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebar = inject(treeSidebarInjectionKey, null);\nconst internalValue = ref(props.defaultValue);\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map());\nconst instance = getCurrentInstance();\n\nconst selectedValue = computed(() => props.modelValue ?? internalValue.value);\nconst resolvedCollapsed = computed(() => props.collapsed ?? sidebar?.collapsed.value ?? false);\n\nconst routerLinkComponent = computed<Component | null>(() => (\n (instance?.appContext.components.RouterLink as Component | undefined) ?? null\n));\n\nconst itemTag = (item: TNavMenuItem) => {\n if (item.to && routerLinkComponent.value) {\n return routerLinkComponent.value;\n }\n return 'button';\n};\n\nconst getInitialFocusedIndex = () => {\n const selectedIndex = props.items.findIndex((item) =>\n item.value === selectedValue.value && !item.disabled,\n );\n\n if (selectedIndex >= 0) {\n return selectedIndex;\n }\n\n return props.items.findIndex((item) => !item.disabled);\n};\n\nconst focusedIndex = ref(getInitialFocusedIndex());\n\nconst rootClasses = computed(() => [\n 't-nav-menu',\n `t-nav-menu--${props.size}`,\n {\n 'is-collapsed': resolvedCollapsed.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst isSelected = (value: string) => selectedValue.value === value;\n\nconst setItemRef = (element: Element | ComponentPublicInstance | null, value: string) => {\n const resolvedElement =\n element instanceof HTMLElement\n ? element\n : element && '$el' in element && element.$el instanceof HTMLElement\n ? element.$el\n : null;\n\n if (resolvedElement) {\n itemRefs.value.set(value, resolvedElement);\n } else {\n itemRefs.value.delete(value);\n }\n};\n\nconst focusItem = (index: number) => {\n const item = props.items[index];\n\n if (!item) {\n return;\n }\n\n focusedIndex.value = index;\n nextTick(() => itemRefs.value.get(item.value)?.focus());\n};\n\nconst selectItem = (item: TNavMenuItem) => {\n if (props.disabled || item.disabled) {\n return;\n }\n\n if (props.modelValue === undefined) {\n internalValue.value = item.value;\n }\n\n emit('update:modelValue', item.value);\n emit('select', item.value, item);\n};\n\nconst onItemKeydown = (event: KeyboardEvent, index: number) => {\n if (props.disabled) {\n return;\n }\n\n if (event.key === 'Home') {\n event.preventDefault();\n const firstIndex = props.items.findIndex((item) => !item.disabled);\n\n if (firstIndex >= 0) {\n focusItem(firstIndex);\n }\n\n return;\n }\n\n if (event.key === 'End') {\n event.preventDefault();\n const lastIndex = [...props.items].reverse().findIndex((item) => !item.disabled);\n\n if (lastIndex >= 0) {\n focusItem(props.items.length - 1 - lastIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n event.preventDefault();\n const nextIndex = getNextEnabledIndex(index, props.items, 1);\n\n if (nextIndex >= 0) {\n focusItem(nextIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n event.preventDefault();\n const previousIndex = getNextEnabledIndex(index, props.items, -1);\n\n if (previousIndex >= 0) {\n focusItem(previousIndex);\n }\n\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectItem(props.items[index]);\n }\n};\n\nconst getTabIndex = (index: number) => (focusedIndex.value === index ? 0 : -1);\n\nwatch(\n () => [props.items, props.modelValue] as const,\n () => {\n const nextIndex = getInitialFocusedIndex();\n focusedIndex.value = nextIndex >= 0 ? nextIndex : -1;\n },\n { deep: true },\n);\n</script>\n\n<template>\n <nav\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n >\n <ul\n v-if=\"items.length > 0\"\n class=\"t-nav-menu__list\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n class=\"t-nav-menu__entry\"\n >\n <component\n :is=\"itemTag(item)\"\n :ref=\"(element: Element | ComponentPublicInstance | null) => setItemRef(element, item.value)\"\n :type=\"item.to ? undefined : 'button'\"\n :to=\"item.to || undefined\"\n class=\"t-nav-menu__item\"\n :class=\"{\n 'is-selected': isSelected(item.value),\n 'is-disabled': disabled || item.disabled,\n }\"\n :disabled=\"(!item.to && (disabled || item.disabled)) || undefined\"\n :aria-disabled=\"(item.to && (disabled || item.disabled)) || undefined\"\n :aria-current=\"isSelected(item.value) ? 'page' : undefined\"\n :tabindex=\"disabled || item.disabled ? -1 : getTabIndex(index)\"\n :title=\"resolvedCollapsed ? item.label : undefined\"\n @click=\"selectItem(item)\"\n @keydown=\"onItemKeydown($event, index)\"\n @focus=\"focusedIndex = index\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :selected=\"isSelected(item.value)\"\n :focused=\"focusedIndex === index\"\n :collapsed=\"resolvedCollapsed\"\n >\n <component\n :is=\"item.icon\"\n v-if=\"item.icon\"\n class=\"t-nav-menu__icon\"\n aria-hidden=\"true\"\n />\n <span\n v-else\n class=\"t-nav-menu__marker\"\n aria-hidden=\"true\"\n >\n {{ (item.shortLabel ?? item.label.charAt(0)).toUpperCase() }}\n </span>\n\n <span class=\"t-nav-menu__copy\">\n <span class=\"t-nav-menu__label\">{{ item.label }}</span>\n <span\n v-if=\"item.description\"\n class=\"t-nav-menu__description\"\n >\n {{ item.description }}\n </span>\n </span>\n\n <span\n v-if=\"item.badge !== undefined && item.badge !== null\"\n class=\"t-nav-menu__badge\"\n >\n {{ item.badge }}\n </span>\n </slot>\n </component>\n </li>\n </ul>\n\n <div\n v-else\n class=\"t-nav-menu__empty\"\n >\n <slot name=\"empty\">\n No navigation items.\n </slot>\n </div>\n </nav>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"TNavMenu.vue_vue_type_script_setup_true_lang.cjs","names":[],"sources":["../../src/components/TNavMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { getNextEnabledIndex, isActivationKey } from '@treeui/utils';\nimport { computed, getCurrentInstance, inject, nextTick, ref, useAttrs, watch, type Component, type ComponentPublicInstance } from 'vue';\nimport type { TSize } from '../types/contracts';\nimport { treeSidebarInjectionKey } from './sidebar';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport interface TNavMenuItem {\n label: string;\n value: string;\n shortLabel?: string;\n description?: string;\n icon?: Component;\n to?: string | Record<string, unknown>;\n badge?: string | number;\n disabled?: boolean;\n /**\n * When the item renders as a router link, controls whether an inclusive\n * (prefix) route match also marks it active. Defaults to the menu-level\n * `exact` prop. Set to `false` for section roots that should stay active on\n * nested routes.\n */\n exact?: boolean;\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n defaultValue?: string;\n items?: TNavMenuItem[];\n size?: TSize;\n collapsed?: boolean;\n disabled?: boolean;\n exact?: boolean;\n }>(),\n {\n modelValue: undefined,\n defaultValue: '',\n items: () => [],\n size: 'md',\n collapsed: undefined,\n disabled: false,\n exact: true,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n select: [value: string, item: TNavMenuItem];\n}>();\n\ndefineSlots<{\n item?: (props: {\n item: TNavMenuItem;\n selected: boolean;\n focused: boolean;\n collapsed: boolean;\n }) => unknown;\n empty?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst sidebar = inject(treeSidebarInjectionKey, null);\nconst internalValue = ref(props.defaultValue);\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map());\nconst instance = getCurrentInstance();\n\nconst selectedValue = computed(() => props.modelValue ?? internalValue.value);\nconst resolvedCollapsed = computed(() => props.collapsed ?? sidebar?.collapsed.value ?? false);\n\nconst routerLinkComponent = computed<Component | null>(() => (\n (instance?.appContext.components.RouterLink as Component | undefined) ?? null\n));\n\nconst itemTag = (item: TNavMenuItem) => {\n if (item.to && routerLinkComponent.value) {\n return routerLinkComponent.value;\n }\n return 'button';\n};\n\nconst isRouterLinkItem = (item: TNavMenuItem) =>\n Boolean(item.to) && routerLinkComponent.value !== null;\n\n// Keep the active highlight deterministic when items render as router links.\n// Vue Router's default `router-link-active` uses inclusive (prefix) matching,\n// so several links can appear active at once. We map the router's active\n// classes onto TreeUI's own `is-selected`:\n// - Controlled menus (explicit `modelValue`) own selection, so the router adds\n// no class of its own — only the bound `is-selected` shows.\n// - Uncontrolled menus let the current route drive selection, using exact\n// matching by default (opt out per item, or menu-wide, via `exact`).\nconst routerActiveClass = (item: TNavMenuItem) => {\n if (!isRouterLinkItem(item)) {\n return undefined;\n }\n\n if (props.modelValue !== undefined) {\n return '';\n }\n\n return (item.exact ?? props.exact) ? '' : 'is-selected';\n};\n\nconst routerExactActiveClass = (item: TNavMenuItem) => {\n if (!isRouterLinkItem(item)) {\n return undefined;\n }\n\n return props.modelValue !== undefined ? '' : 'is-selected';\n};\n\nconst getInitialFocusedIndex = () => {\n const selectedIndex = props.items.findIndex((item) =>\n item.value === selectedValue.value && !item.disabled,\n );\n\n if (selectedIndex >= 0) {\n return selectedIndex;\n }\n\n return props.items.findIndex((item) => !item.disabled);\n};\n\nconst focusedIndex = ref(getInitialFocusedIndex());\n\nconst rootClasses = computed(() => [\n 't-nav-menu',\n `t-nav-menu--${props.size}`,\n {\n 'is-collapsed': resolvedCollapsed.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst rootAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst isSelected = (value: string) => selectedValue.value === value;\n\nconst setItemRef = (element: Element | ComponentPublicInstance | null, value: string) => {\n const resolvedElement =\n element instanceof HTMLElement\n ? element\n : element && '$el' in element && element.$el instanceof HTMLElement\n ? element.$el\n : null;\n\n if (resolvedElement) {\n itemRefs.value.set(value, resolvedElement);\n } else {\n itemRefs.value.delete(value);\n }\n};\n\nconst focusItem = (index: number) => {\n const item = props.items[index];\n\n if (!item) {\n return;\n }\n\n focusedIndex.value = index;\n nextTick(() => itemRefs.value.get(item.value)?.focus());\n};\n\nconst selectItem = (item: TNavMenuItem) => {\n if (props.disabled || item.disabled) {\n return;\n }\n\n if (props.modelValue === undefined) {\n internalValue.value = item.value;\n }\n\n emit('update:modelValue', item.value);\n emit('select', item.value, item);\n};\n\nconst onItemKeydown = (event: KeyboardEvent, index: number) => {\n if (props.disabled) {\n return;\n }\n\n if (event.key === 'Home') {\n event.preventDefault();\n const firstIndex = props.items.findIndex((item) => !item.disabled);\n\n if (firstIndex >= 0) {\n focusItem(firstIndex);\n }\n\n return;\n }\n\n if (event.key === 'End') {\n event.preventDefault();\n const lastIndex = [...props.items].reverse().findIndex((item) => !item.disabled);\n\n if (lastIndex >= 0) {\n focusItem(props.items.length - 1 - lastIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n event.preventDefault();\n const nextIndex = getNextEnabledIndex(index, props.items, 1);\n\n if (nextIndex >= 0) {\n focusItem(nextIndex);\n }\n\n return;\n }\n\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n event.preventDefault();\n const previousIndex = getNextEnabledIndex(index, props.items, -1);\n\n if (previousIndex >= 0) {\n focusItem(previousIndex);\n }\n\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectItem(props.items[index]);\n }\n};\n\nconst getTabIndex = (index: number) => (focusedIndex.value === index ? 0 : -1);\n\nwatch(\n () => [props.items, props.modelValue] as const,\n () => {\n const nextIndex = getInitialFocusedIndex();\n focusedIndex.value = nextIndex >= 0 ? nextIndex : -1;\n },\n { deep: true },\n);\n</script>\n\n<template>\n <nav\n v-bind=\"rootAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n >\n <ul\n v-if=\"items.length > 0\"\n class=\"t-nav-menu__list\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n class=\"t-nav-menu__entry\"\n >\n <component\n :is=\"itemTag(item)\"\n :ref=\"(element: Element | ComponentPublicInstance | null) => setItemRef(element, item.value)\"\n :type=\"item.to ? undefined : 'button'\"\n :to=\"item.to || undefined\"\n class=\"t-nav-menu__item\"\n :class=\"{\n 'is-selected': isSelected(item.value),\n 'is-disabled': disabled || item.disabled,\n }\"\n :active-class=\"routerActiveClass(item)\"\n :exact-active-class=\"routerExactActiveClass(item)\"\n :disabled=\"(!item.to && (disabled || item.disabled)) || undefined\"\n :aria-disabled=\"(item.to && (disabled || item.disabled)) || undefined\"\n :aria-current=\"isSelected(item.value) ? 'page' : undefined\"\n :tabindex=\"disabled || item.disabled ? -1 : getTabIndex(index)\"\n :title=\"resolvedCollapsed ? item.label : undefined\"\n @click=\"selectItem(item)\"\n @keydown=\"onItemKeydown($event, index)\"\n @focus=\"focusedIndex = index\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :selected=\"isSelected(item.value)\"\n :focused=\"focusedIndex === index\"\n :collapsed=\"resolvedCollapsed\"\n >\n <component\n :is=\"item.icon\"\n v-if=\"item.icon\"\n class=\"t-nav-menu__icon\"\n aria-hidden=\"true\"\n />\n <span\n v-else\n class=\"t-nav-menu__marker\"\n aria-hidden=\"true\"\n >\n {{ (item.shortLabel ?? item.label.charAt(0)).toUpperCase() }}\n </span>\n\n <span class=\"t-nav-menu__copy\">\n <span class=\"t-nav-menu__label\">{{ item.label }}</span>\n <span\n v-if=\"item.description\"\n class=\"t-nav-menu__description\"\n >\n {{ item.description }}\n </span>\n </span>\n\n <span\n v-if=\"item.badge !== undefined && item.badge !== null\"\n class=\"t-nav-menu__badge\"\n >\n {{ item.badge }}\n </span>\n </slot>\n </component>\n </li>\n </ul>\n\n <div\n v-else\n class=\"t-nav-menu__empty\"\n >\n <slot name=\"empty\">\n No navigation items.\n </slot>\n </div>\n </nav>\n</template>\n"],"mappings":"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"}