@morscherlab/mld-sdk 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AuditTrail.vue.d.ts +1 -10
- package/dist/components/AuditTrail.vue.js.map +1 -1
- package/dist/components/BatchProgressList.vue.d.ts +1 -17
- package/dist/components/BatchProgressList.vue.js.map +1 -1
- package/dist/components/Breadcrumb.vue.d.ts +1 -5
- package/dist/components/Breadcrumb.vue.js.map +1 -1
- package/dist/components/DateTimePicker.vue.js +0 -1
- package/dist/components/DateTimePicker.vue.js.map +1 -1
- package/dist/components/MoleculeInput.vue.d.ts +1 -4
- package/dist/components/MoleculeInput.vue.js.map +1 -1
- package/dist/components/RackEditor.vue.js +2 -2
- package/dist/components/RackEditor.vue.js.map +1 -1
- package/dist/components/ReagentList.vue.d.ts +1 -15
- package/dist/components/ReagentList.vue.js.map +1 -1
- package/dist/components/SampleHierarchyTree.vue.d.ts +1 -12
- package/dist/components/SampleHierarchyTree.vue.js.map +1 -1
- package/dist/components/ScheduleCalendar.vue.js.map +1 -1
- package/dist/components/ScientificNumber.vue.d.ts +1 -1
- package/dist/components/ScientificNumber.vue.js.map +1 -1
- package/dist/components/SettingsModal.vue.d.ts +1 -5
- package/dist/components/SettingsModal.vue.js.map +1 -1
- package/dist/components/StepWizard.vue.d.ts +1 -8
- package/dist/components/StepWizard.vue.js.map +1 -1
- package/dist/components/UnitInput.vue.d.ts +1 -6
- package/dist/components/UnitInput.vue.js.map +1 -1
- package/dist/styles.css +20 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/AuditTrail.vue +1 -12
- package/src/components/BatchProgressList.vue +1 -20
- package/src/components/Breadcrumb.vue +1 -5
- package/src/components/DateTimePicker.vue +1 -1
- package/src/components/MoleculeInput.story.vue +1 -1
- package/src/components/MoleculeInput.vue +1 -5
- package/src/components/RackEditor.vue +2 -2
- package/src/components/ReagentList.story.vue +1 -1
- package/src/components/ReagentList.vue +1 -26
- package/src/components/SampleHierarchyTree.story.vue +1 -1
- package/src/components/SampleHierarchyTree.vue +1 -25
- package/src/components/ScheduleCalendar.vue +1 -1
- package/src/components/ScientificNumber.story.vue +1 -2
- package/src/components/ScientificNumber.vue +1 -2
- package/src/components/SettingsModal.vue +1 -7
- package/src/components/StepWizard.vue +1 -10
- package/src/components/UnitInput.vue +1 -7
- package/src/styles/components/button.css +2 -0
- package/src/styles/components/datetime-picker.css +4 -0
- package/src/styles/components/rack-editor.css +6 -0
- package/src/types/index.ts +10 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReagentList.vue.js","sources":["../../src/components/ReagentList.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\n\nexport type StorageCondition = 'RT' | '4C' | '-20C' | '-80C'\n\nexport type ReagentColumn =\n | 'name'\n | 'catalog'\n | 'lot'\n | 'expiry'\n | 'storage'\n | 'location'\n | 'stock'\n | 'supplier'\n\nexport interface Reagent {\n id: string\n name: string\n catalogNumber?: string\n lotNumber?: string\n expiryDate?: Date | string\n storageCondition?: StorageCondition\n location?: string\n stockLevel?: number\n stockUnit?: string\n supplier?: string\n url?: string\n}\n\ninterface Props {\n modelValue?: Reagent[]\n readonly?: boolean\n showStockLevel?: boolean\n lowStockThreshold?: number\n columns?: ReagentColumn[]\n sortable?: boolean\n searchable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n readonly: false,\n showStockLevel: true,\n lowStockThreshold: 10,\n columns: () => ['name', 'lot', 'expiry', 'storage', 'stock'],\n sortable: true,\n searchable: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [reagents: Reagent[]]\n 'add': []\n 'edit': [reagent: Reagent]\n 'remove': [reagentId: string]\n 'reorder': [reagentId: string, fromIndex: number, toIndex: number]\n}>()\n\n// State\nconst searchQuery = ref('')\nconst sortColumn = ref<ReagentColumn | null>(null)\nconst sortDirection = ref<'asc' | 'desc'>('asc')\nconst draggedId = ref<string | null>(null)\nconst dragOverId = ref<string | null>(null)\n\n// Column configuration\nconst columnLabels: Record<ReagentColumn, string> = {\n name: 'Name',\n catalog: 'Catalog #',\n lot: 'Lot #',\n expiry: 'Expiry',\n storage: 'Storage',\n location: 'Location',\n stock: 'Stock',\n supplier: 'Supplier',\n}\n\n// Computed\nconst filteredReagents = computed(() => {\n let result = [...(props.modelValue || [])]\n\n // Filter by search query\n if (searchQuery.value) {\n const query = searchQuery.value.toLowerCase()\n result = result.filter(\n (r) =>\n r.name.toLowerCase().includes(query) ||\n r.catalogNumber?.toLowerCase().includes(query) ||\n r.lotNumber?.toLowerCase().includes(query) ||\n r.supplier?.toLowerCase().includes(query)\n )\n }\n\n // Sort\n if (sortColumn.value) {\n const col = sortColumn.value\n result.sort((a, b) => {\n let aVal: string | number | null | undefined = getColumnValue(a, col) as string | number | null | undefined\n let bVal: string | number | null | undefined = getColumnValue(b, col) as string | number | null | undefined\n\n if (aVal === null || aVal === undefined) return 1\n if (bVal === null || bVal === undefined) return -1\n\n if (typeof aVal === 'string') aVal = aVal.toLowerCase()\n if (typeof bVal === 'string') bVal = bVal.toLowerCase()\n\n if (aVal < bVal) return sortDirection.value === 'asc' ? -1 : 1\n if (aVal > bVal) return sortDirection.value === 'asc' ? 1 : -1\n return 0\n })\n }\n\n return result\n})\n\nfunction getColumnValue(reagent: Reagent, column: ReagentColumn): unknown {\n switch (column) {\n case 'name':\n return reagent.name\n case 'catalog':\n return reagent.catalogNumber\n case 'lot':\n return reagent.lotNumber\n case 'expiry':\n return reagent.expiryDate ? new Date(reagent.expiryDate).getTime() : null\n case 'storage':\n return reagent.storageCondition\n case 'location':\n return reagent.location\n case 'stock':\n return reagent.stockLevel\n case 'supplier':\n return reagent.supplier\n default:\n return null\n }\n}\n\n// Helpers\nfunction isExpired(reagent: Reagent): boolean {\n if (!reagent.expiryDate) return false\n const expiry = new Date(reagent.expiryDate)\n return expiry < new Date()\n}\n\nfunction isExpiringSoon(reagent: Reagent, daysThreshold = 30): boolean {\n if (!reagent.expiryDate) return false\n const expiry = new Date(reagent.expiryDate)\n const now = new Date()\n const daysUntilExpiry = (expiry.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)\n return daysUntilExpiry > 0 && daysUntilExpiry <= daysThreshold\n}\n\nfunction isLowStock(reagent: Reagent): boolean {\n if (reagent.stockLevel === undefined) return false\n return reagent.stockLevel <= props.lowStockThreshold\n}\n\nfunction formatExpiryDate(date: Date | string | undefined): string {\n if (!date) return '-'\n const d = new Date(date)\n return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short' })\n}\n\nfunction getStockLevel(reagent: Reagent): number {\n return reagent.stockLevel ?? 0\n}\n\nfunction getStockFillClass(level: number): string {\n if (level >= 50) return 'mld-reagent-list__stock-fill--good'\n if (level >= 20) return 'mld-reagent-list__stock-fill--warning'\n return 'mld-reagent-list__stock-fill--low'\n}\n\nfunction getRowClass(reagent: Reagent): string[] {\n const classes: string[] = ['mld-reagent-list__row']\n if (isExpired(reagent)) classes.push('mld-reagent-list__row--expired')\n else if (isExpiringSoon(reagent)) classes.push('mld-reagent-list__row--expiring-soon')\n if (isLowStock(reagent)) classes.push('mld-reagent-list__row--low-stock')\n if (draggedId.value === reagent.id) classes.push('mld-reagent-list__row--dragging')\n if (dragOverId.value === reagent.id) classes.push('mld-reagent-list__row--drag-over')\n return classes\n}\n\n// Actions\nfunction handleSort(column: ReagentColumn) {\n if (!props.sortable) return\n if (sortColumn.value === column) {\n sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'\n } else {\n sortColumn.value = column\n sortDirection.value = 'asc'\n }\n}\n\nfunction handleEdit(reagent: Reagent) {\n emit('edit', reagent)\n}\n\nfunction handleRemove(reagentId: string) {\n emit('remove', reagentId)\n const updated = (props.modelValue || []).filter((r) => r.id !== reagentId)\n emit('update:modelValue', updated)\n}\n\nfunction handleAdd() {\n emit('add')\n}\n\n// Drag and drop\nfunction handleDragStart(event: DragEvent, reagent: Reagent) {\n if (props.readonly || !props.sortable) return\n draggedId.value = reagent.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', reagent.id)\n }\n}\n\nfunction handleDragOver(event: DragEvent, reagent: Reagent) {\n if (props.readonly || !props.sortable || !draggedId.value) return\n event.preventDefault()\n dragOverId.value = reagent.id\n}\n\nfunction handleDragLeave() {\n dragOverId.value = null\n}\n\nfunction handleDrop(event: DragEvent, targetReagent: Reagent) {\n if (props.readonly || !props.sortable || !draggedId.value) return\n event.preventDefault()\n\n const reagents = [...(props.modelValue || [])]\n const fromIndex = reagents.findIndex((r) => r.id === draggedId.value)\n const toIndex = reagents.findIndex((r) => r.id === targetReagent.id)\n\n if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) {\n draggedId.value = null\n dragOverId.value = null\n return\n }\n\n const [removed] = reagents.splice(fromIndex, 1)\n reagents.splice(toIndex, 0, removed)\n\n emit('reorder', draggedId.value, fromIndex, toIndex)\n emit('update:modelValue', reagents)\n\n draggedId.value = null\n dragOverId.value = null\n}\n\nfunction handleDragEnd() {\n draggedId.value = null\n dragOverId.value = null\n}\n</script>\n\n<template>\n <div class=\"mld-reagent-list\">\n <!-- Header -->\n <div class=\"mld-reagent-list__header\">\n <!-- Search -->\n <div v-if=\"searchable\" class=\"mld-reagent-list__search\">\n <svg class=\"mld-reagent-list__search-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n class=\"mld-reagent-list__search-input\"\n placeholder=\"Search reagents...\"\n />\n </div>\n\n <!-- Add button -->\n <button\n v-if=\"!readonly\"\n type=\"button\"\n class=\"mld-reagent-list__add-btn\"\n @click=\"handleAdd\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Add\n </button>\n </div>\n\n <!-- Table -->\n <table v-if=\"filteredReagents.length > 0\" class=\"mld-reagent-list__table\">\n <thead>\n <tr>\n <th v-if=\"sortable && !readonly\" style=\"width: 24px\" />\n <th\n v-for=\"col in columns\"\n :key=\"col\"\n :class=\"[sortable ? 'mld-reagent-list__col--sortable' : '']\"\n @click=\"handleSort(col)\"\n >\n {{ columnLabels[col] }}\n <svg\n v-if=\"sortable\"\n :class=\"[\n 'mld-reagent-list__sort-icon',\n sortColumn === col ? 'mld-reagent-list__sort-icon--active' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n v-if=\"sortColumn === col && sortDirection === 'desc'\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M19 9l-7 7-7-7\"\n />\n <path\n v-else\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M5 15l7-7 7 7\"\n />\n </svg>\n </th>\n <th v-if=\"!readonly\" style=\"width: 80px\" />\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"reagent in filteredReagents\"\n :key=\"reagent.id\"\n :class=\"getRowClass(reagent)\"\n :draggable=\"sortable && !readonly\"\n @dragstart=\"handleDragStart($event, reagent)\"\n @dragover=\"handleDragOver($event, reagent)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, reagent)\"\n @dragend=\"handleDragEnd\"\n >\n <!-- Drag handle -->\n <td v-if=\"sortable && !readonly\">\n <svg class=\"mld-reagent-list__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n </td>\n\n <!-- Name -->\n <td v-if=\"columns.includes('name')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--name\">\n <a\n v-if=\"reagent.url\"\n :href=\"reagent.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"mld-reagent-list__link\"\n >\n {{ reagent.name }}\n </a>\n <span v-else>{{ reagent.name }}</span>\n </td>\n\n <!-- Catalog number -->\n <td v-if=\"columns.includes('catalog')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--muted\">\n {{ reagent.catalogNumber || '-' }}\n </td>\n\n <!-- Lot number -->\n <td v-if=\"columns.includes('lot')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--muted\">\n {{ reagent.lotNumber || '-' }}\n </td>\n\n <!-- Expiry -->\n <td v-if=\"columns.includes('expiry')\" class=\"mld-reagent-list__cell\">\n <span v-if=\"isExpired(reagent)\" class=\"mld-reagent-list__expired-text\">\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n <span v-else-if=\"isExpiringSoon(reagent)\" class=\"mld-reagent-list__expiring-text\">\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n <span v-else>\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n\n <!-- Expiry badge -->\n <span v-if=\"isExpired(reagent)\" class=\"mld-reagent-list__badge mld-reagent-list__badge--error\">\n <svg class=\"mld-reagent-list__badge-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n Expired\n </span>\n <span v-else-if=\"isExpiringSoon(reagent)\" class=\"mld-reagent-list__badge mld-reagent-list__badge--warning\">\n <svg class=\"mld-reagent-list__badge-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n Soon\n </span>\n </td>\n\n <!-- Storage -->\n <td v-if=\"columns.includes('storage')\" class=\"mld-reagent-list__cell\">\n <span\n v-if=\"reagent.storageCondition\"\n :class=\"['mld-reagent-list__storage', `mld-reagent-list__storage--${reagent.storageCondition}`]\"\n >\n {{ reagent.storageCondition }}\n </span>\n <span v-else class=\"mld-reagent-list__cell--muted\">-</span>\n </td>\n\n <!-- Location -->\n <td v-if=\"columns.includes('location')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--muted\">\n {{ reagent.location || '-' }}\n </td>\n\n <!-- Stock -->\n <td v-if=\"columns.includes('stock') && showStockLevel\" class=\"mld-reagent-list__cell\">\n <div v-if=\"reagent.stockLevel !== undefined\" class=\"mld-reagent-list__stock\">\n <div class=\"mld-reagent-list__stock-bar\">\n <div\n :class=\"['mld-reagent-list__stock-fill', getStockFillClass(getStockLevel(reagent))]\"\n :style=\"{ width: `${Math.min(100, getStockLevel(reagent))}%` }\"\n />\n </div>\n <span class=\"mld-reagent-list__stock-text\">\n {{ getStockLevel(reagent) }}%\n </span>\n </div>\n <span v-else class=\"mld-reagent-list__cell--muted\">-</span>\n </td>\n\n <!-- Supplier -->\n <td v-if=\"columns.includes('supplier')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--muted\">\n {{ reagent.supplier || '-' }}\n </td>\n\n <!-- Actions -->\n <td v-if=\"!readonly\" class=\"mld-reagent-list__cell mld-reagent-list__cell--actions\">\n <button\n type=\"button\"\n class=\"mld-reagent-list__action-btn\"\n aria-label=\"Edit\"\n @click=\"handleEdit(reagent)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mld-reagent-list__action-btn mld-reagent-list__action-btn--danger\"\n aria-label=\"Remove\"\n @click=\"handleRemove(reagent.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\" />\n </svg>\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Empty state -->\n <div v-else class=\"mld-reagent-list__empty\">\n <svg class=\"mld-reagent-list__empty-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\" />\n </svg>\n <p class=\"mld-reagent-list__empty-text\">\n {{ searchQuery ? 'No reagents found' : 'No reagents added yet' }}\n </p>\n <button\n v-if=\"!readonly && !searchQuery\"\n type=\"button\"\n class=\"mld-reagent-list__empty-btn\"\n @click=\"handleAdd\"\n >\n Add Reagent\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/reagent-list.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_Fragment","_renderList","_normalizeClass","_toDisplayString"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ReagentList.vue.js","sources":["../../src/components/ReagentList.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport type { ReagentColumn, Reagent } from '../types'\n\ninterface Props {\n modelValue?: Reagent[]\n readonly?: boolean\n showStockLevel?: boolean\n lowStockThreshold?: number\n columns?: ReagentColumn[]\n sortable?: boolean\n searchable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n readonly: false,\n showStockLevel: true,\n lowStockThreshold: 10,\n columns: () => ['name', 'lot', 'expiry', 'storage', 'stock'],\n sortable: true,\n searchable: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [reagents: Reagent[]]\n 'add': []\n 'edit': [reagent: Reagent]\n 'remove': [reagentId: string]\n 'reorder': [reagentId: string, fromIndex: number, toIndex: number]\n}>()\n\n// State\nconst searchQuery = ref('')\nconst sortColumn = ref<ReagentColumn | null>(null)\nconst sortDirection = ref<'asc' | 'desc'>('asc')\nconst draggedId = ref<string | null>(null)\nconst dragOverId = ref<string | null>(null)\n\n// Column configuration\nconst columnLabels: Record<ReagentColumn, string> = {\n name: 'Name',\n catalog: 'Catalog #',\n lot: 'Lot #',\n expiry: 'Expiry',\n storage: 'Storage',\n location: 'Location',\n stock: 'Stock',\n supplier: 'Supplier',\n}\n\n// Computed\nconst filteredReagents = computed(() => {\n let result = [...(props.modelValue || [])]\n\n // Filter by search query\n if (searchQuery.value) {\n const query = searchQuery.value.toLowerCase()\n result = result.filter(\n (r) =>\n r.name.toLowerCase().includes(query) ||\n r.catalogNumber?.toLowerCase().includes(query) ||\n r.lotNumber?.toLowerCase().includes(query) ||\n r.supplier?.toLowerCase().includes(query)\n )\n }\n\n // Sort\n if (sortColumn.value) {\n const col = sortColumn.value\n result.sort((a, b) => {\n let aVal: string | number | null | undefined = getColumnValue(a, col) as string | number | null | undefined\n let bVal: string | number | null | undefined = getColumnValue(b, col) as string | number | null | undefined\n\n if (aVal === null || aVal === undefined) return 1\n if (bVal === null || bVal === undefined) return -1\n\n if (typeof aVal === 'string') aVal = aVal.toLowerCase()\n if (typeof bVal === 'string') bVal = bVal.toLowerCase()\n\n if (aVal < bVal) return sortDirection.value === 'asc' ? -1 : 1\n if (aVal > bVal) return sortDirection.value === 'asc' ? 1 : -1\n return 0\n })\n }\n\n return result\n})\n\nfunction getColumnValue(reagent: Reagent, column: ReagentColumn): unknown {\n switch (column) {\n case 'name':\n return reagent.name\n case 'catalog':\n return reagent.catalogNumber\n case 'lot':\n return reagent.lotNumber\n case 'expiry':\n return reagent.expiryDate ? new Date(reagent.expiryDate).getTime() : null\n case 'storage':\n return reagent.storageCondition\n case 'location':\n return reagent.location\n case 'stock':\n return reagent.stockLevel\n case 'supplier':\n return reagent.supplier\n default:\n return null\n }\n}\n\n// Helpers\nfunction isExpired(reagent: Reagent): boolean {\n if (!reagent.expiryDate) return false\n const expiry = new Date(reagent.expiryDate)\n return expiry < new Date()\n}\n\nfunction isExpiringSoon(reagent: Reagent, daysThreshold = 30): boolean {\n if (!reagent.expiryDate) return false\n const expiry = new Date(reagent.expiryDate)\n const now = new Date()\n const daysUntilExpiry = (expiry.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)\n return daysUntilExpiry > 0 && daysUntilExpiry <= daysThreshold\n}\n\nfunction isLowStock(reagent: Reagent): boolean {\n if (reagent.stockLevel === undefined) return false\n return reagent.stockLevel <= props.lowStockThreshold\n}\n\nfunction formatExpiryDate(date: Date | string | undefined): string {\n if (!date) return '-'\n const d = new Date(date)\n return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short' })\n}\n\nfunction getStockLevel(reagent: Reagent): number {\n return reagent.stockLevel ?? 0\n}\n\nfunction getStockFillClass(level: number): string {\n if (level >= 50) return 'mld-reagent-list__stock-fill--good'\n if (level >= 20) return 'mld-reagent-list__stock-fill--warning'\n return 'mld-reagent-list__stock-fill--low'\n}\n\nfunction getRowClass(reagent: Reagent): string[] {\n const classes: string[] = ['mld-reagent-list__row']\n if (isExpired(reagent)) classes.push('mld-reagent-list__row--expired')\n else if (isExpiringSoon(reagent)) classes.push('mld-reagent-list__row--expiring-soon')\n if (isLowStock(reagent)) classes.push('mld-reagent-list__row--low-stock')\n if (draggedId.value === reagent.id) classes.push('mld-reagent-list__row--dragging')\n if (dragOverId.value === reagent.id) classes.push('mld-reagent-list__row--drag-over')\n return classes\n}\n\n// Actions\nfunction handleSort(column: ReagentColumn) {\n if (!props.sortable) return\n if (sortColumn.value === column) {\n sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'\n } else {\n sortColumn.value = column\n sortDirection.value = 'asc'\n }\n}\n\nfunction handleEdit(reagent: Reagent) {\n emit('edit', reagent)\n}\n\nfunction handleRemove(reagentId: string) {\n emit('remove', reagentId)\n const updated = (props.modelValue || []).filter((r) => r.id !== reagentId)\n emit('update:modelValue', updated)\n}\n\nfunction handleAdd() {\n emit('add')\n}\n\n// Drag and drop\nfunction handleDragStart(event: DragEvent, reagent: Reagent) {\n if (props.readonly || !props.sortable) return\n draggedId.value = reagent.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', reagent.id)\n }\n}\n\nfunction handleDragOver(event: DragEvent, reagent: Reagent) {\n if (props.readonly || !props.sortable || !draggedId.value) return\n event.preventDefault()\n dragOverId.value = reagent.id\n}\n\nfunction handleDragLeave() {\n dragOverId.value = null\n}\n\nfunction handleDrop(event: DragEvent, targetReagent: Reagent) {\n if (props.readonly || !props.sortable || !draggedId.value) return\n event.preventDefault()\n\n const reagents = [...(props.modelValue || [])]\n const fromIndex = reagents.findIndex((r) => r.id === draggedId.value)\n const toIndex = reagents.findIndex((r) => r.id === targetReagent.id)\n\n if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) {\n draggedId.value = null\n dragOverId.value = null\n return\n }\n\n const [removed] = reagents.splice(fromIndex, 1)\n reagents.splice(toIndex, 0, removed)\n\n emit('reorder', draggedId.value, fromIndex, toIndex)\n emit('update:modelValue', reagents)\n\n draggedId.value = null\n dragOverId.value = null\n}\n\nfunction handleDragEnd() {\n draggedId.value = null\n dragOverId.value = null\n}\n</script>\n\n<template>\n <div class=\"mld-reagent-list\">\n <!-- Header -->\n <div class=\"mld-reagent-list__header\">\n <!-- Search -->\n <div v-if=\"searchable\" class=\"mld-reagent-list__search\">\n <svg class=\"mld-reagent-list__search-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n class=\"mld-reagent-list__search-input\"\n placeholder=\"Search reagents...\"\n />\n </div>\n\n <!-- Add button -->\n <button\n v-if=\"!readonly\"\n type=\"button\"\n class=\"mld-reagent-list__add-btn\"\n @click=\"handleAdd\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Add\n </button>\n </div>\n\n <!-- Table -->\n <table v-if=\"filteredReagents.length > 0\" class=\"mld-reagent-list__table\">\n <thead>\n <tr>\n <th v-if=\"sortable && !readonly\" style=\"width: 24px\" />\n <th\n v-for=\"col in columns\"\n :key=\"col\"\n :class=\"[sortable ? 'mld-reagent-list__col--sortable' : '']\"\n @click=\"handleSort(col)\"\n >\n {{ columnLabels[col] }}\n <svg\n v-if=\"sortable\"\n :class=\"[\n 'mld-reagent-list__sort-icon',\n sortColumn === col ? 'mld-reagent-list__sort-icon--active' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n v-if=\"sortColumn === col && sortDirection === 'desc'\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M19 9l-7 7-7-7\"\n />\n <path\n v-else\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M5 15l7-7 7 7\"\n />\n </svg>\n </th>\n <th v-if=\"!readonly\" style=\"width: 80px\" />\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"reagent in filteredReagents\"\n :key=\"reagent.id\"\n :class=\"getRowClass(reagent)\"\n :draggable=\"sortable && !readonly\"\n @dragstart=\"handleDragStart($event, reagent)\"\n @dragover=\"handleDragOver($event, reagent)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, reagent)\"\n @dragend=\"handleDragEnd\"\n >\n <!-- Drag handle -->\n <td v-if=\"sortable && !readonly\">\n <svg class=\"mld-reagent-list__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n </td>\n\n <!-- Name -->\n <td v-if=\"columns.includes('name')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--name\">\n <a\n v-if=\"reagent.url\"\n :href=\"reagent.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"mld-reagent-list__link\"\n >\n {{ reagent.name }}\n </a>\n <span v-else>{{ reagent.name }}</span>\n </td>\n\n <!-- Catalog number -->\n <td v-if=\"columns.includes('catalog')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--muted\">\n {{ reagent.catalogNumber || '-' }}\n </td>\n\n <!-- Lot number -->\n <td v-if=\"columns.includes('lot')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--muted\">\n {{ reagent.lotNumber || '-' }}\n </td>\n\n <!-- Expiry -->\n <td v-if=\"columns.includes('expiry')\" class=\"mld-reagent-list__cell\">\n <span v-if=\"isExpired(reagent)\" class=\"mld-reagent-list__expired-text\">\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n <span v-else-if=\"isExpiringSoon(reagent)\" class=\"mld-reagent-list__expiring-text\">\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n <span v-else>\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n\n <!-- Expiry badge -->\n <span v-if=\"isExpired(reagent)\" class=\"mld-reagent-list__badge mld-reagent-list__badge--error\">\n <svg class=\"mld-reagent-list__badge-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n Expired\n </span>\n <span v-else-if=\"isExpiringSoon(reagent)\" class=\"mld-reagent-list__badge mld-reagent-list__badge--warning\">\n <svg class=\"mld-reagent-list__badge-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n Soon\n </span>\n </td>\n\n <!-- Storage -->\n <td v-if=\"columns.includes('storage')\" class=\"mld-reagent-list__cell\">\n <span\n v-if=\"reagent.storageCondition\"\n :class=\"['mld-reagent-list__storage', `mld-reagent-list__storage--${reagent.storageCondition}`]\"\n >\n {{ reagent.storageCondition }}\n </span>\n <span v-else class=\"mld-reagent-list__cell--muted\">-</span>\n </td>\n\n <!-- Location -->\n <td v-if=\"columns.includes('location')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--muted\">\n {{ reagent.location || '-' }}\n </td>\n\n <!-- Stock -->\n <td v-if=\"columns.includes('stock') && showStockLevel\" class=\"mld-reagent-list__cell\">\n <div v-if=\"reagent.stockLevel !== undefined\" class=\"mld-reagent-list__stock\">\n <div class=\"mld-reagent-list__stock-bar\">\n <div\n :class=\"['mld-reagent-list__stock-fill', getStockFillClass(getStockLevel(reagent))]\"\n :style=\"{ width: `${Math.min(100, getStockLevel(reagent))}%` }\"\n />\n </div>\n <span class=\"mld-reagent-list__stock-text\">\n {{ getStockLevel(reagent) }}%\n </span>\n </div>\n <span v-else class=\"mld-reagent-list__cell--muted\">-</span>\n </td>\n\n <!-- Supplier -->\n <td v-if=\"columns.includes('supplier')\" class=\"mld-reagent-list__cell mld-reagent-list__cell--muted\">\n {{ reagent.supplier || '-' }}\n </td>\n\n <!-- Actions -->\n <td v-if=\"!readonly\" class=\"mld-reagent-list__cell mld-reagent-list__cell--actions\">\n <button\n type=\"button\"\n class=\"mld-reagent-list__action-btn\"\n aria-label=\"Edit\"\n @click=\"handleEdit(reagent)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mld-reagent-list__action-btn mld-reagent-list__action-btn--danger\"\n aria-label=\"Remove\"\n @click=\"handleRemove(reagent.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\" />\n </svg>\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Empty state -->\n <div v-else class=\"mld-reagent-list__empty\">\n <svg class=\"mld-reagent-list__empty-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\" />\n </svg>\n <p class=\"mld-reagent-list__empty-text\">\n {{ searchQuery ? 'No reagents found' : 'No reagents added yet' }}\n </p>\n <button\n v-if=\"!readonly && !searchQuery\"\n type=\"button\"\n class=\"mld-reagent-list__empty-btn\"\n @click=\"handleAdd\"\n >\n Add Reagent\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/reagent-list.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_Fragment","_renderList","_normalizeClass","_toDisplayString"],"mappings":"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"}
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export type BadgeVariant = 'default' | 'success' | 'warning' | 'error';
|
|
3
|
-
export interface TreeNode {
|
|
4
|
-
id: string;
|
|
5
|
-
label: string;
|
|
6
|
-
type?: TreeNodeType;
|
|
7
|
-
icon?: string;
|
|
8
|
-
children?: TreeNode[];
|
|
9
|
-
metadata?: Record<string, unknown>;
|
|
10
|
-
badge?: string | number;
|
|
11
|
-
badgeVariant?: BadgeVariant;
|
|
12
|
-
}
|
|
1
|
+
import { TreeNode } from '../types';
|
|
13
2
|
interface Props {
|
|
14
3
|
nodes: TreeNode[];
|
|
15
4
|
defaultExpandedIds?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SampleHierarchyTree.vue.js","sources":["../../src/components/SampleHierarchyTree.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, type VNode } from 'vue'\nimport { h, Transition } from 'vue'\n\nexport type TreeNodeType =\n | 'study'\n | 'experiment'\n | 'plate'\n | 'sample'\n | 'cell_line'\n | 'passage'\n | 'clone'\n | 'treatment'\n | 'folder'\n | 'custom'\n\nexport type BadgeVariant = 'default' | 'success' | 'warning' | 'error'\n\nexport interface TreeNode {\n id: string\n label: string\n type?: TreeNodeType\n icon?: string\n children?: TreeNode[]\n metadata?: Record<string, unknown>\n badge?: string | number\n badgeVariant?: BadgeVariant\n}\n\ninterface Props {\n nodes: TreeNode[]\n defaultExpandedIds?: string[]\n expandAll?: boolean\n showIcons?: boolean\n showCounts?: boolean\n maxDepth?: number\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n defaultExpandedIds: () => [],\n expandAll: false,\n showIcons: true,\n showCounts: true,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'node-click': [node: TreeNode]\n 'expand': [nodeId: string]\n 'collapse': [nodeId: string]\n}>()\n\n// Track expanded nodes\nconst expandedIds = ref<Set<string>>(new Set(props.defaultExpandedIds))\n\n// Default icons by type (Lucide SVG paths - arrays to support multi-path icons)\ninterface IconElement {\n tag: 'path' | 'circle' | 'rect'\n attrs: Record<string, string | number>\n}\n\nconst typeIcons: Record<TreeNodeType, IconElement[]> = {\n study: [\n { tag: 'path', attrs: { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z' } },\n ],\n experiment: [\n { tag: 'path', attrs: { d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' } },\n { tag: 'path', attrs: { d: 'M6.453 15h11.094' } },\n { tag: 'path', attrs: { d: 'M8.5 2h7' } },\n ],\n plate: [\n { tag: 'rect', attrs: { width: '7', height: '7', x: '3', y: '3', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '14', y: '3', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '14', y: '14', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '3', y: '14', rx: '1' } },\n ],\n sample: [\n { tag: 'circle', attrs: { cx: '12', cy: '12', r: '10' } },\n { tag: 'path', attrs: { d: 'M12 6v6l4 2' } },\n ],\n cell_line: [\n { tag: 'path', attrs: { d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5' } },\n { tag: 'path', attrs: { d: 'M9 18h6' } },\n { tag: 'path', attrs: { d: 'M10 22h4' } },\n ],\n passage: [\n { tag: 'path', attrs: { d: 'M3 5h.01' } },\n { tag: 'path', attrs: { d: 'M3 12h.01' } },\n { tag: 'path', attrs: { d: 'M3 19h.01' } },\n { tag: 'path', attrs: { d: 'M8 5h13' } },\n { tag: 'path', attrs: { d: 'M8 12h13' } },\n { tag: 'path', attrs: { d: 'M8 19h13' } },\n ],\n clone: [\n { tag: 'rect', attrs: { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2' } },\n { tag: 'path', attrs: { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' } },\n ],\n treatment: [\n { tag: 'path', attrs: { d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' } },\n { tag: 'path', attrs: { d: 'M6.453 15h11.094' } },\n { tag: 'path', attrs: { d: 'M8.5 2h7' } },\n ],\n folder: [\n { tag: 'path', attrs: { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z' } },\n ],\n custom: [\n { tag: 'path', attrs: { d: 'M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915' } },\n { tag: 'circle', attrs: { cx: '12', cy: '12', r: '3' } },\n ],\n}\n\n// Collect all node IDs for expand all\nfunction collectAllIds(nodes: TreeNode[]): string[] {\n const ids: string[] = []\n function traverse(node: TreeNode) {\n ids.push(node.id)\n if (node.children) {\n node.children.forEach(traverse)\n }\n }\n nodes.forEach(traverse)\n return ids\n}\n\n// Watch for expandAll changes\nwatch(\n () => props.expandAll,\n (shouldExpandAll) => {\n if (shouldExpandAll) {\n expandedIds.value = new Set(collectAllIds(props.nodes))\n } else {\n expandedIds.value = new Set(props.defaultExpandedIds)\n }\n },\n { immediate: true }\n)\n\nfunction isExpanded(nodeId: string): boolean {\n return expandedIds.value.has(nodeId)\n}\n\nfunction toggleExpand(node: TreeNode) {\n if (isExpanded(node.id)) {\n expandedIds.value.delete(node.id)\n emit('collapse', node.id)\n } else {\n expandedIds.value.add(node.id)\n emit('expand', node.id)\n }\n}\n\nfunction handleNodeClick(node: TreeNode) {\n emit('node-click', node)\n}\n\nfunction getIconElements(node: TreeNode): IconElement[] {\n if (node.icon) return [{ tag: 'path', attrs: { d: node.icon } }]\n return typeIcons[node.type || 'custom']\n}\n\nfunction getBadgeContent(node: TreeNode): string | number | undefined {\n if (node.badge !== undefined) return node.badge\n if (props.showCounts && node.children && node.children.length > 0) {\n return node.children.length\n }\n return undefined\n}\n\nfunction getBadgeVariant(node: TreeNode): BadgeVariant {\n return node.badgeVariant || 'default'\n}\n\nfunction hasChildren(node: TreeNode): boolean {\n return !!node.children && node.children.length > 0\n}\n\nfunction canShowChildren(node: TreeNode, depth: number): boolean {\n if (!hasChildren(node)) return false\n if (props.maxDepth !== undefined && depth >= props.maxDepth) return false\n return isExpanded(node.id)\n}\n\n// Render tree node recursively\nfunction renderNode(node: TreeNode, depth: number): VNode {\n const expanded = isExpanded(node.id)\n const canExpand = hasChildren(node)\n const showChildNodes = canShowChildren(node, depth)\n const badge = getBadgeContent(node)\n const badgeVar = getBadgeVariant(node)\n const iconElements = getIconElements(node)\n\n const header = h(\n 'div',\n {\n class: 'mld-sample-tree__node-header',\n onClick: () => handleNodeClick(node),\n },\n [\n canExpand\n ? h(\n 'button',\n {\n type: 'button',\n class: 'mld-sample-tree__toggle',\n 'aria-label': expanded ? 'Collapse' : 'Expand',\n onClick: (e: Event) => {\n e.stopPropagation()\n toggleExpand(node)\n },\n },\n [\n h('svg', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' }, [\n h('path', { d: 'm9 18 6-6-6-6' }),\n ]),\n ]\n )\n : h('span', { class: 'mld-sample-tree__toggle-placeholder' }),\n props.showIcons\n ? h('span', { class: ['mld-sample-tree__icon', `mld-sample-tree__icon--${node.type || 'custom'}`] }, [\n h('svg', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' },\n iconElements.map(el => h(el.tag, el.attrs))\n ),\n ])\n : null,\n h('span', { class: 'mld-sample-tree__label' }, node.label),\n badge !== undefined\n ? h('span', { class: ['mld-sample-tree__badge', `mld-sample-tree__badge--${badgeVar}`] }, String(badge))\n : null,\n ]\n )\n\n const childNodes =\n showChildNodes && node.children\n ? h(\n Transition,\n { enterActiveClass: 'mld-sample-tree__children--entering', leaveActiveClass: 'mld-sample-tree__children--leaving' },\n () => h('div', { class: 'mld-sample-tree__children' }, node.children!.map((child) => renderNode(child, depth + 1)))\n )\n : null\n\n return h(\n 'div',\n {\n key: node.id,\n class: ['mld-sample-tree__node', expanded ? 'mld-sample-tree__node--expanded' : '', !canExpand ? 'mld-sample-tree__node--leaf' : ''],\n role: 'treeitem',\n 'aria-expanded': canExpand ? expanded : undefined,\n },\n [header, childNodes]\n )\n}\n\n// Render the entire tree\nfunction renderTree(): VNode[] {\n return props.nodes.map((node) => renderNode(node, 0))\n}\n</script>\n\n<template>\n <div :class=\"['mld-sample-tree', `mld-sample-tree--${size}`]\" role=\"tree\">\n <!-- Empty state -->\n <div v-if=\"nodes.length === 0\" class=\"mld-sample-tree__empty\">\n <svg class=\"mld-sample-tree__empty-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\" />\n </svg>\n <p class=\"mld-sample-tree__empty-text\">No items</p>\n </div>\n\n <!-- Tree nodes rendered via render function -->\n <template v-else>\n <component :is=\"() => renderTree()\" />\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/sample-hierarchy-tree.css';\n</style>\n"],"names":["_createElementBlock","_openBlock","_createElementVNode","_createBlock","_resolveDynamicComponent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SampleHierarchyTree.vue.js","sources":["../../src/components/SampleHierarchyTree.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, type VNode } from 'vue'\nimport { h, Transition } from 'vue'\nimport type { TreeNodeType, BadgeVariant, TreeNode } from '../types'\n\ninterface Props {\n nodes: TreeNode[]\n defaultExpandedIds?: string[]\n expandAll?: boolean\n showIcons?: boolean\n showCounts?: boolean\n maxDepth?: number\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n defaultExpandedIds: () => [],\n expandAll: false,\n showIcons: true,\n showCounts: true,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'node-click': [node: TreeNode]\n 'expand': [nodeId: string]\n 'collapse': [nodeId: string]\n}>()\n\n// Track expanded nodes\nconst expandedIds = ref<Set<string>>(new Set(props.defaultExpandedIds))\n\n// Default icons by type (Lucide SVG paths - arrays to support multi-path icons)\ninterface IconElement {\n tag: 'path' | 'circle' | 'rect'\n attrs: Record<string, string | number>\n}\n\nconst typeIcons: Record<TreeNodeType, IconElement[]> = {\n study: [\n { tag: 'path', attrs: { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z' } },\n ],\n experiment: [\n { tag: 'path', attrs: { d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' } },\n { tag: 'path', attrs: { d: 'M6.453 15h11.094' } },\n { tag: 'path', attrs: { d: 'M8.5 2h7' } },\n ],\n plate: [\n { tag: 'rect', attrs: { width: '7', height: '7', x: '3', y: '3', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '14', y: '3', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '14', y: '14', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '3', y: '14', rx: '1' } },\n ],\n sample: [\n { tag: 'circle', attrs: { cx: '12', cy: '12', r: '10' } },\n { tag: 'path', attrs: { d: 'M12 6v6l4 2' } },\n ],\n cell_line: [\n { tag: 'path', attrs: { d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5' } },\n { tag: 'path', attrs: { d: 'M9 18h6' } },\n { tag: 'path', attrs: { d: 'M10 22h4' } },\n ],\n passage: [\n { tag: 'path', attrs: { d: 'M3 5h.01' } },\n { tag: 'path', attrs: { d: 'M3 12h.01' } },\n { tag: 'path', attrs: { d: 'M3 19h.01' } },\n { tag: 'path', attrs: { d: 'M8 5h13' } },\n { tag: 'path', attrs: { d: 'M8 12h13' } },\n { tag: 'path', attrs: { d: 'M8 19h13' } },\n ],\n clone: [\n { tag: 'rect', attrs: { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2' } },\n { tag: 'path', attrs: { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' } },\n ],\n treatment: [\n { tag: 'path', attrs: { d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' } },\n { tag: 'path', attrs: { d: 'M6.453 15h11.094' } },\n { tag: 'path', attrs: { d: 'M8.5 2h7' } },\n ],\n folder: [\n { tag: 'path', attrs: { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z' } },\n ],\n custom: [\n { tag: 'path', attrs: { d: 'M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915' } },\n { tag: 'circle', attrs: { cx: '12', cy: '12', r: '3' } },\n ],\n}\n\n// Collect all node IDs for expand all\nfunction collectAllIds(nodes: TreeNode[]): string[] {\n const ids: string[] = []\n function traverse(node: TreeNode) {\n ids.push(node.id)\n if (node.children) {\n node.children.forEach(traverse)\n }\n }\n nodes.forEach(traverse)\n return ids\n}\n\n// Watch for expandAll changes\nwatch(\n () => props.expandAll,\n (shouldExpandAll) => {\n if (shouldExpandAll) {\n expandedIds.value = new Set(collectAllIds(props.nodes))\n } else {\n expandedIds.value = new Set(props.defaultExpandedIds)\n }\n },\n { immediate: true }\n)\n\nfunction isExpanded(nodeId: string): boolean {\n return expandedIds.value.has(nodeId)\n}\n\nfunction toggleExpand(node: TreeNode) {\n if (isExpanded(node.id)) {\n expandedIds.value.delete(node.id)\n emit('collapse', node.id)\n } else {\n expandedIds.value.add(node.id)\n emit('expand', node.id)\n }\n}\n\nfunction handleNodeClick(node: TreeNode) {\n emit('node-click', node)\n}\n\nfunction getIconElements(node: TreeNode): IconElement[] {\n if (node.icon) return [{ tag: 'path', attrs: { d: node.icon } }]\n return typeIcons[node.type || 'custom']\n}\n\nfunction getBadgeContent(node: TreeNode): string | number | undefined {\n if (node.badge !== undefined) return node.badge\n if (props.showCounts && node.children && node.children.length > 0) {\n return node.children.length\n }\n return undefined\n}\n\nfunction getBadgeVariant(node: TreeNode): BadgeVariant {\n return node.badgeVariant || 'default'\n}\n\nfunction hasChildren(node: TreeNode): boolean {\n return !!node.children && node.children.length > 0\n}\n\nfunction canShowChildren(node: TreeNode, depth: number): boolean {\n if (!hasChildren(node)) return false\n if (props.maxDepth !== undefined && depth >= props.maxDepth) return false\n return isExpanded(node.id)\n}\n\n// Render tree node recursively\nfunction renderNode(node: TreeNode, depth: number): VNode {\n const expanded = isExpanded(node.id)\n const canExpand = hasChildren(node)\n const showChildNodes = canShowChildren(node, depth)\n const badge = getBadgeContent(node)\n const badgeVar = getBadgeVariant(node)\n const iconElements = getIconElements(node)\n\n const header = h(\n 'div',\n {\n class: 'mld-sample-tree__node-header',\n onClick: () => handleNodeClick(node),\n },\n [\n canExpand\n ? h(\n 'button',\n {\n type: 'button',\n class: 'mld-sample-tree__toggle',\n 'aria-label': expanded ? 'Collapse' : 'Expand',\n onClick: (e: Event) => {\n e.stopPropagation()\n toggleExpand(node)\n },\n },\n [\n h('svg', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' }, [\n h('path', { d: 'm9 18 6-6-6-6' }),\n ]),\n ]\n )\n : h('span', { class: 'mld-sample-tree__toggle-placeholder' }),\n props.showIcons\n ? h('span', { class: ['mld-sample-tree__icon', `mld-sample-tree__icon--${node.type || 'custom'}`] }, [\n h('svg', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' },\n iconElements.map(el => h(el.tag, el.attrs))\n ),\n ])\n : null,\n h('span', { class: 'mld-sample-tree__label' }, node.label),\n badge !== undefined\n ? h('span', { class: ['mld-sample-tree__badge', `mld-sample-tree__badge--${badgeVar}`] }, String(badge))\n : null,\n ]\n )\n\n const childNodes =\n showChildNodes && node.children\n ? h(\n Transition,\n { enterActiveClass: 'mld-sample-tree__children--entering', leaveActiveClass: 'mld-sample-tree__children--leaving' },\n () => h('div', { class: 'mld-sample-tree__children' }, node.children!.map((child) => renderNode(child, depth + 1)))\n )\n : null\n\n return h(\n 'div',\n {\n key: node.id,\n class: ['mld-sample-tree__node', expanded ? 'mld-sample-tree__node--expanded' : '', !canExpand ? 'mld-sample-tree__node--leaf' : ''],\n role: 'treeitem',\n 'aria-expanded': canExpand ? expanded : undefined,\n },\n [header, childNodes]\n )\n}\n\n// Render the entire tree\nfunction renderTree(): VNode[] {\n return props.nodes.map((node) => renderNode(node, 0))\n}\n</script>\n\n<template>\n <div :class=\"['mld-sample-tree', `mld-sample-tree--${size}`]\" role=\"tree\">\n <!-- Empty state -->\n <div v-if=\"nodes.length === 0\" class=\"mld-sample-tree__empty\">\n <svg class=\"mld-sample-tree__empty-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\" />\n </svg>\n <p class=\"mld-sample-tree__empty-text\">No items</p>\n </div>\n\n <!-- Tree nodes rendered via render function -->\n <template v-else>\n <component :is=\"() => renderTree()\" />\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/sample-hierarchy-tree.css';\n</style>\n"],"names":["_createElementBlock","_openBlock","_createElementVNode","_createBlock","_resolveDynamicComponent"],"mappings":";;;;;;;;;;;;;;;;;;AAeA,UAAM,QAAQ;AAQd,UAAM,OAAO;AAOb,UAAM,cAAc,IAAiB,IAAI,IAAI,MAAM,kBAAkB,CAAC;AAQtE,UAAM,YAAiD;AAAA,MACrD,OAAO;AAAA,QACL,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,2HAAyH;AAAA,MAAE;AAAA,MAExJ,YAAY;AAAA,QACV,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,4GAA0G;AAAA,QACrI,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,qBAAmB;AAAA,QAC9C,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,aAAW;AAAA,MAAE;AAAA,MAE1C,OAAO;AAAA,QACL,EAAE,KAAK,QAAQ,OAAO,EAAE,OAAO,KAAK,QAAQ,KAAK,GAAG,KAAK,GAAG,KAAK,IAAI,MAAI;AAAA,QACzE,EAAE,KAAK,QAAQ,OAAO,EAAE,OAAO,KAAK,QAAQ,KAAK,GAAG,MAAM,GAAG,KAAK,IAAI,MAAI;AAAA,QAC1E,EAAE,KAAK,QAAQ,OAAO,EAAE,OAAO,KAAK,QAAQ,KAAK,GAAG,MAAM,GAAG,MAAM,IAAI,MAAI;AAAA,QAC3E,EAAE,KAAK,QAAQ,OAAO,EAAE,OAAO,KAAK,QAAQ,KAAK,GAAG,KAAK,GAAG,MAAM,IAAI,MAAI;AAAA,MAAE;AAAA,MAE9E,QAAQ;AAAA,QACN,EAAE,KAAK,UAAU,OAAO,EAAE,IAAI,MAAM,IAAI,MAAM,GAAG,OAAK;AAAA,QACtD,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,gBAAc;AAAA,MAAE;AAAA,MAE7C,WAAW;AAAA,QACT,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,yGAAuG;AAAA,QAClI,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,YAAU;AAAA,QACrC,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,aAAW;AAAA,MAAE;AAAA,MAE1C,SAAS;AAAA,QACP,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,aAAW;AAAA,QACtC,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,cAAY;AAAA,QACvC,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,cAAY;AAAA,QACvC,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,YAAU;AAAA,QACrC,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,aAAW;AAAA,QACtC,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,aAAW;AAAA,MAAE;AAAA,MAE1C,OAAO;AAAA,QACL,EAAE,KAAK,QAAQ,OAAO,EAAE,OAAO,MAAM,QAAQ,MAAM,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,IAAI,MAAI;AAAA,QACpF,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,4DAA0D;AAAA,MAAE;AAAA,MAEzF,WAAW;AAAA,QACT,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,4GAA0G;AAAA,QACrI,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,qBAAmB;AAAA,QAC9C,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,aAAW;AAAA,MAAE;AAAA,MAE1C,QAAQ;AAAA,QACN,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,2HAAyH;AAAA,MAAE;AAAA,MAExJ,QAAQ;AAAA,QACN,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,4UAA0U;AAAA,QACrW,EAAE,KAAK,UAAU,OAAO,EAAE,IAAI,MAAM,IAAI,MAAM,GAAG,IAAA,EAAI;AAAA,MAAE;AAAA,IACzD;AAIF,aAAS,cAAc,OAA6B;AAClD,YAAM,MAAgB,CAAA;AACtB,eAAS,SAAS,MAAgB;AAChC,YAAI,KAAK,KAAK,EAAE;AAChB,YAAI,KAAK,UAAU;AACjB,eAAK,SAAS,QAAQ,QAAQ;AAAA,QAChC;AAAA,MACF;AACA,YAAM,QAAQ,QAAQ;AACtB,aAAO;AAAA,IACT;AAGA;AAAA,MACE,MAAM,MAAM;AAAA,MACZ,CAAC,oBAAoB;AACnB,YAAI,iBAAiB;AACnB,sBAAY,QAAQ,IAAI,IAAI,cAAc,MAAM,KAAK,CAAC;AAAA,QACxD,OAAO;AACL,sBAAY,QAAQ,IAAI,IAAI,MAAM,kBAAkB;AAAA,QACtD;AAAA,MACF;AAAA,MACA,EAAE,WAAW,KAAA;AAAA,IAAK;AAGpB,aAAS,WAAW,QAAyB;AAC3C,aAAO,YAAY,MAAM,IAAI,MAAM;AAAA,IACrC;AAEA,aAAS,aAAa,MAAgB;AACpC,UAAI,WAAW,KAAK,EAAE,GAAG;AACvB,oBAAY,MAAM,OAAO,KAAK,EAAE;AAChC,aAAK,YAAY,KAAK,EAAE;AAAA,MAC1B,OAAO;AACL,oBAAY,MAAM,IAAI,KAAK,EAAE;AAC7B,aAAK,UAAU,KAAK,EAAE;AAAA,MACxB;AAAA,IACF;AAEA,aAAS,gBAAgB,MAAgB;AACvC,WAAK,cAAc,IAAI;AAAA,IACzB;AAEA,aAAS,gBAAgB,MAA+B;AACtD,UAAI,KAAK,KAAM,QAAO,CAAC,EAAE,KAAK,QAAQ,OAAO,EAAE,GAAG,KAAK,KAAA,GAAQ;AAC/D,aAAO,UAAU,KAAK,QAAQ,QAAQ;AAAA,IACxC;AAEA,aAAS,gBAAgB,MAA6C;AACpE,UAAI,KAAK,UAAU,OAAW,QAAO,KAAK;AAC1C,UAAI,MAAM,cAAc,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;AACjE,eAAO,KAAK,SAAS;AAAA,MACvB;AACA,aAAO;AAAA,IACT;AAEA,aAAS,gBAAgB,MAA8B;AACrD,aAAO,KAAK,gBAAgB;AAAA,IAC9B;AAEA,aAAS,YAAY,MAAyB;AAC5C,aAAO,CAAC,CAAC,KAAK,YAAY,KAAK,SAAS,SAAS;AAAA,IACnD;AAEA,aAAS,gBAAgB,MAAgB,OAAwB;AAC/D,UAAI,CAAC,YAAY,IAAI,EAAG,QAAO;AAC/B,UAAI,MAAM,aAAa,UAAa,SAAS,MAAM,SAAU,QAAO;AACpE,aAAO,WAAW,KAAK,EAAE;AAAA,IAC3B;AAGA,aAAS,WAAW,MAAgB,OAAsB;AACxD,YAAM,WAAW,WAAW,KAAK,EAAE;AACnC,YAAM,YAAY,YAAY,IAAI;AAClC,YAAM,iBAAiB,gBAAgB,MAAM,KAAK;AAClD,YAAM,QAAQ,gBAAgB,IAAI;AAClC,YAAM,WAAW,gBAAgB,IAAI;AACrC,YAAM,eAAe,gBAAgB,IAAI;AAEzC,YAAM,SAAS;AAAA,QACb;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,SAAS,MAAM,gBAAgB,IAAI;AAAA,QAAA;AAAA,QAErC;AAAA,UACE,YACI;AAAA,YACE;AAAA,YACA;AAAA,cACE,MAAM;AAAA,cACN,OAAO;AAAA,cACP,cAAc,WAAW,aAAa;AAAA,cACtC,SAAS,CAAC,MAAa;AACrB,kBAAE,gBAAA;AACF,6BAAa,IAAI;AAAA,cACnB;AAAA,YAAA;AAAA,YAEF;AAAA,cACE,EAAE,OAAO,EAAE,MAAM,QAAQ,QAAQ,gBAAgB,SAAS,aAAa,gBAAgB,KAAK,kBAAkB,SAAS,mBAAmB,WAAW;AAAA,gBACnJ,EAAE,QAAQ,EAAE,GAAG,iBAAiB;AAAA,cAAA,CACjC;AAAA,YAAA;AAAA,UACH,IAEF,EAAE,QAAQ,EAAE,OAAO,uCAAuC;AAAA,UAC9D,MAAM,YACF,EAAE,QAAQ,EAAE,OAAO,CAAC,yBAAyB,0BAA0B,KAAK,QAAQ,QAAQ,EAAE,KAAK;AAAA,YACjG;AAAA,cAAE;AAAA,cAAO,EAAE,MAAM,QAAQ,QAAQ,gBAAgB,SAAS,aAAa,gBAAgB,KAAK,kBAAkB,SAAS,mBAAmB,QAAA;AAAA,cACxI,aAAa,IAAI,CAAA,OAAM,EAAE,GAAG,KAAK,GAAG,KAAK,CAAC;AAAA,YAAA;AAAA,UAC5C,CACD,IACD;AAAA,UACJ,EAAE,QAAQ,EAAE,OAAO,yBAAA,GAA4B,KAAK,KAAK;AAAA,UACzD,UAAU,SACN,EAAE,QAAQ,EAAE,OAAO,CAAC,0BAA0B,2BAA2B,QAAQ,EAAE,EAAA,GAAK,OAAO,KAAK,CAAC,IACrG;AAAA,QAAA;AAAA,MACN;AAGF,YAAM,aACJ,kBAAkB,KAAK,WACnB;AAAA,QACE;AAAA,QACA,EAAE,kBAAkB,uCAAuC,kBAAkB,qCAAA;AAAA,QAC7E,MAAM,EAAE,OAAO,EAAE,OAAO,+BAA+B,KAAK,SAAU,IAAI,CAAC,UAAU,WAAW,OAAO,QAAQ,CAAC,CAAC,CAAC;AAAA,MAAA,IAEpH;AAEN,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,KAAK,KAAK;AAAA,UACV,OAAO,CAAC,yBAAyB,WAAW,oCAAoC,IAAI,CAAC,YAAY,gCAAgC,EAAE;AAAA,UACnI,MAAM;AAAA,UACN,iBAAiB,YAAY,WAAW;AAAA,QAAA;AAAA,QAE1C,CAAC,QAAQ,UAAU;AAAA,MAAA;AAAA,IAEvB;AAGA,aAAS,aAAsB;AAC7B,aAAO,MAAM,MAAM,IAAI,CAAC,SAAS,WAAW,MAAM,CAAC,CAAC;AAAA,IACtD;;0BAIEA,mBAaM,OAAA;AAAA,QAbA,8DAA+C,QAAA,IAAI,EAAA,CAAA;AAAA,QAAK,MAAK;AAAA,MAAA;QAEtD,QAAA,MAAM,WAAM,KAAvBC,UAAA,GAAAD,mBAKM,OALN,YAKM,CAAA,GAAA,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA;AAAA,UAJJE,mBAEM,OAAA;AAAA,YAFD,OAAM;AAAA,YAA8B,MAAK;AAAA,YAAO,QAAO;AAAA,YAAe,SAAQ;AAAA,YAAY,gBAAa;AAAA,YAAI,kBAAe;AAAA,YAAQ,mBAAgB;AAAA,UAAA;YACrJA,mBAAmI,QAAA,EAA7H,GAAE,0HAAwH;AAAA,UAAA;UAElIA,mBAAmD,KAAA,EAAhD,OAAM,8BAAA,GAA8B,YAAQ,EAAA;AAAA,QAAA,SAK/CD,UAAA,GAAAE,YAAsCC,8BAAhB,YAAU,GAAA,EAAA,KAAA,GAAA;AAAA,MAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScheduleCalendar.vue.js","sources":["../../src/components/ScheduleCalendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch, onMounted, onUnmounted } from 'vue'\nimport type {\n ScheduleView,\n ScheduleEvent,\n ScheduleBlockedSlot,\n ScheduleEventStatus,\n} from '../types/components'\nimport { formatTime } from '../composables/useTimeUtils'\nimport { useScheduleDrag } from '../composables/useScheduleDrag'\n\ninterface Props {\n modelValue?: Date | string\n view?: ScheduleView\n events?: ScheduleEvent[]\n dayStartHour?: number\n dayEndHour?: number\n slotDuration?: number\n showNowIndicator?: boolean\n readonly?: boolean\n blockedSlots?: ScheduleBlockedSlot[]\n weekStartsOn?: 0 | 1\n showViewToggle?: boolean\n showNavigation?: boolean\n statusColors?: Record<string, string>\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n view: 'week',\n events: () => [],\n dayStartHour: 6,\n dayEndHour: 22,\n slotDuration: 30,\n showNowIndicator: true,\n readonly: false,\n blockedSlots: () => [],\n weekStartsOn: 1,\n showViewToggle: true,\n showNavigation: true,\n locale: 'en-US',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: Date]\n 'update:view': [value: ScheduleView]\n 'event-click': [event: ScheduleEvent]\n 'event-create': [context: { start: Date; end: Date }]\n 'event-update': [context: { event: ScheduleEvent; newStart: Date; newEnd: Date }]\n 'slot-click': [context: { date: Date; hour: number; minute: number }]\n 'navigate': [context: { direction: string; date: Date }]\n}>()\n\nconst SLOT_HEIGHT = 40\nconst currentView = ref<ScheduleView>(props.view)\nconst currentDate = ref<Date>(parseModelDate(props.modelValue))\nconst nowTimer = ref<ReturnType<typeof setInterval>>()\nconst now = ref(new Date())\nconst bodyRef = ref<HTMLDivElement>()\n\nwatch(() => props.view, (v) => { currentView.value = v })\nwatch(() => props.modelValue, (v) => { if (v) currentDate.value = parseModelDate(v) })\n\nconst slotHeightRef = computed(() => SLOT_HEIGHT)\nconst slotDurationRef = computed(() => props.slotDuration)\nconst dayStartRef = computed(() => props.dayStartHour)\nconst dayEndRef = computed(() => props.dayEndHour)\nconst readonlyRef = computed(() => props.readonly)\n\nconst { isDragging, ghost, startCreate, startMove, startResize } = useScheduleDrag({\n slotDuration: slotDurationRef,\n dayStartHour: dayStartRef,\n dayEndHour: dayEndRef,\n readonly: readonlyRef,\n slotHeight: slotHeightRef,\n onCreateComplete(start, end) {\n emit('event-create', { start, end })\n },\n onMoveComplete(event, newStart, newEnd) {\n emit('event-update', { event, newStart, newEnd })\n },\n onResizeComplete(event, newStart, newEnd) {\n emit('event-update', { event, newStart, newEnd })\n },\n})\n\nconst totalSlots = computed(() => {\n return ((props.dayEndHour - props.dayStartHour) * 60) / props.slotDuration\n})\n\nconst timeLabels = computed(() => {\n const labels: string[] = []\n for (let i = 0; i <= totalSlots.value; i++) {\n const totalMinutes = props.dayStartHour * 60 + i * props.slotDuration\n const hour = Math.floor(totalMinutes / 60)\n const minute = totalMinutes % 60\n labels.push(formatTime(hour, minute, '24h'))\n }\n return labels\n})\n\nconst weekDays = computed(() => {\n const days: Date[] = []\n const start = getWeekStart(currentDate.value)\n for (let i = 0; i < 7; i++) {\n const d = new Date(start)\n d.setDate(d.getDate() + i)\n days.push(d)\n }\n return days\n})\n\nconst visibleDays = computed(() => {\n if (currentView.value === 'day') return [currentDate.value]\n if (currentView.value === 'week') return weekDays.value\n return []\n})\n\nconst headerTitle = computed(() => {\n if (currentView.value === 'day') {\n return currentDate.value.toLocaleDateString(props.locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })\n }\n if (currentView.value === 'week') {\n const days = weekDays.value\n const first = days[0]\n const last = days[6]\n const sameMonth = first.getMonth() === last.getMonth()\n if (sameMonth) {\n return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.getDate()}, ${last.getFullYear()}`\n }\n return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })}, ${last.getFullYear()}`\n }\n return currentDate.value.toLocaleDateString(props.locale, { year: 'numeric', month: 'long' })\n})\n\n// Month view computations\nconst monthDays = computed(() => {\n const year = currentDate.value.getFullYear()\n const month = currentDate.value.getMonth()\n const firstDay = new Date(year, month, 1)\n const lastDay = new Date(year, month + 1, 0)\n\n const days: { date: Date; isCurrentMonth: boolean }[] = []\n\n // Adjust for weekStartsOn\n let startPadding = firstDay.getDay() - props.weekStartsOn\n if (startPadding < 0) startPadding += 7\n\n for (let i = startPadding - 1; i >= 0; i--) {\n const date = new Date(year, month, -i)\n days.push({ date, isCurrentMonth: false })\n }\n\n for (let i = 1; i <= lastDay.getDate(); i++) {\n days.push({ date: new Date(year, month, i), isCurrentMonth: true })\n }\n\n const remaining = 42 - days.length\n for (let i = 1; i <= remaining; i++) {\n days.push({ date: new Date(year, month + 1, i), isCurrentMonth: false })\n }\n\n return days\n})\n\nconst monthWeekdayLabels = computed(() => {\n const labels: string[] = []\n const base = new Date(2024, 0, props.weekStartsOn === 1 ? 1 : 7) // A known Monday or Sunday\n for (let i = 0; i < 7; i++) {\n const d = new Date(base)\n d.setDate(d.getDate() + i)\n labels.push(d.toLocaleDateString(props.locale, { weekday: 'short' }))\n }\n return labels\n})\n\nfunction parseModelDate(value?: Date | string): Date {\n if (!value) return new Date()\n if (value instanceof Date) return value\n const d = new Date(value)\n return isNaN(d.getTime()) ? new Date() : d\n}\n\nfunction getWeekStart(date: Date): Date {\n const d = new Date(date)\n const day = d.getDay()\n const diff = (day - props.weekStartsOn + 7) % 7\n d.setDate(d.getDate() - diff)\n return d\n}\n\nfunction isToday(date: Date): boolean {\n return date.toDateString() === now.value.toDateString()\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return a.toDateString() === b.toDateString()\n}\n\nfunction navigate(direction: 'prev' | 'next' | 'today') {\n const d = new Date(currentDate.value)\n if (direction === 'today') {\n currentDate.value = new Date()\n } else {\n const delta = direction === 'prev' ? -1 : 1\n if (currentView.value === 'day') d.setDate(d.getDate() + delta)\n else if (currentView.value === 'week') d.setDate(d.getDate() + delta * 7)\n else d.setMonth(d.getMonth() + delta)\n currentDate.value = d\n }\n emit('update:modelValue', currentDate.value)\n emit('navigate', { direction, date: currentDate.value })\n}\n\nfunction setView(view: ScheduleView) {\n currentView.value = view\n emit('update:view', view)\n}\n\nfunction getEventStyle(event: ScheduleEvent, dayDate: Date) {\n const eventStart = new Date(event.start)\n const eventEnd = new Date(event.end)\n if (!isSameDay(eventStart, dayDate) && !isSameDay(eventEnd, dayDate)) return null\n\n const dayStartMin = props.dayStartHour * 60\n const startMin = Math.max(eventStart.getHours() * 60 + eventStart.getMinutes(), dayStartMin) - dayStartMin\n const endMin = Math.min(eventEnd.getHours() * 60 + eventEnd.getMinutes(), props.dayEndHour * 60) - dayStartMin\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return {\n top: `${startMin * pixelsPerMin}px`,\n height: `${Math.max((endMin - startMin) * pixelsPerMin, SLOT_HEIGHT / 2)}px`,\n }\n}\n\nfunction getEventsForDay(day: Date): ScheduleEvent[] {\n return props.events.filter((e) => {\n const start = new Date(e.start)\n return isSameDay(start, day)\n })\n}\n\nfunction getEventsForDate(date: Date): ScheduleEvent[] {\n return props.events.filter((e) => {\n const start = new Date(e.start)\n return isSameDay(start, date)\n })\n}\n\nfunction getBlockedForDay(day: Date): ScheduleBlockedSlot[] {\n return props.blockedSlots.filter((b) => {\n const start = new Date(b.start)\n return isSameDay(start, day)\n })\n}\n\nfunction getBlockedStyle(slot: ScheduleBlockedSlot) {\n const start = new Date(slot.start)\n const end = new Date(slot.end)\n const dayStartMin = props.dayStartHour * 60\n const startMin = Math.max(start.getHours() * 60 + start.getMinutes(), dayStartMin) - dayStartMin\n const endMin = Math.min(end.getHours() * 60 + end.getMinutes(), props.dayEndHour * 60) - dayStartMin\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return {\n top: `${startMin * pixelsPerMin}px`,\n height: `${(endMin - startMin) * pixelsPerMin}px`,\n }\n}\n\nfunction getNowIndicatorStyle(): Record<string, string> | null {\n if (!props.showNowIndicator) return null\n const nowMin = now.value.getHours() * 60 + now.value.getMinutes()\n const dayStartMin = props.dayStartHour * 60\n const dayEndMin = props.dayEndHour * 60\n if (nowMin < dayStartMin || nowMin > dayEndMin) return null\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return { top: `${(nowMin - dayStartMin) * pixelsPerMin}px` }\n}\n\nfunction getStatusClass(status?: ScheduleEventStatus): string {\n if (!status) return 'mld-schedule__event--confirmed'\n return `mld-schedule__event--${status}`\n}\n\nfunction getMonthStatusClass(status?: ScheduleEventStatus): string {\n if (!status) return 'mld-schedule__month-event--confirmed'\n return `mld-schedule__month-event--${status}`\n}\n\nfunction onEventClick(event: ScheduleEvent, e: MouseEvent) {\n e.stopPropagation()\n emit('event-click', event)\n}\n\nfunction onSlotClick(day: Date, slotIndex: number) {\n if (props.readonly || isDragging.value) return\n const totalMinutes = props.dayStartHour * 60 + slotIndex * props.slotDuration\n const hour = Math.floor(totalMinutes / 60)\n const minute = totalMinutes % 60\n const date = new Date(day)\n date.setHours(hour, minute, 0, 0)\n emit('slot-click', { date, hour, minute })\n}\n\nfunction onSlotPointerDown(day: Date, slotIndex: number, e: PointerEvent) {\n if (props.readonly) return\n const date = new Date(day)\n const totalMinutes = props.dayStartHour * 60 + slotIndex * props.slotDuration\n date.setHours(Math.floor(totalMinutes / 60), totalMinutes % 60, 0, 0)\n const dayIndex = visibleDays.value.findIndex((d) => isSameDay(d, day))\n startCreate(date, e.clientY, dayIndex)\n}\n\nfunction onEventPointerDown(event: ScheduleEvent, dayIndex: number, e: PointerEvent) {\n e.stopPropagation()\n startMove(event, e.clientY, dayIndex)\n}\n\nfunction onResizePointerDown(event: ScheduleEvent, edge: 'top' | 'bottom', dayIndex: number, e: PointerEvent) {\n e.stopPropagation()\n startResize(event, edge, e.clientY, dayIndex)\n}\n\nfunction onMonthDayClick(date: Date) {\n currentDate.value = date\n emit('update:modelValue', date)\n setView('day')\n}\n\nfunction formatEventTime(event: ScheduleEvent): string {\n const start = new Date(event.start)\n const end = new Date(event.end)\n return `${formatTime(start.getHours(), start.getMinutes())} - ${formatTime(end.getHours(), end.getMinutes())}`\n}\n\nonMounted(() => {\n nowTimer.value = setInterval(() => { now.value = new Date() }, 60000)\n\n // Scroll to current time or 8am\n if (bodyRef.value) {\n const targetHour = now.value.getHours() >= props.dayStartHour && now.value.getHours() <= props.dayEndHour\n ? now.value.getHours()\n : 8\n const offsetMin = (targetHour - props.dayStartHour) * 60\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n bodyRef.value.scrollTop = Math.max(0, offsetMin * pixelsPerMin - 100)\n }\n})\n\nonUnmounted(() => {\n if (nowTimer.value) clearInterval(nowTimer.value)\n})\n</script>\n\n<template>\n <div class=\"mld-schedule\" :style=\"{ '--slot-height': `${SLOT_HEIGHT}px` } as any\">\n <!-- Header -->\n <div v-if=\"showNavigation || showViewToggle\" class=\"mld-schedule__header\">\n <div v-if=\"showNavigation\" class=\"mld-schedule__nav\">\n <button\n type=\"button\"\n class=\"mld-schedule__nav-btn\"\n aria-label=\"Previous\"\n @click=\"navigate('prev')\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mld-schedule__nav-btn\"\n aria-label=\"Next\"\n @click=\"navigate('next')\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mld-schedule__today-btn\"\n @click=\"navigate('today')\"\n >\n Today\n </button>\n </div>\n\n <span class=\"mld-schedule__title\">{{ headerTitle }}</span>\n\n <div v-if=\"showViewToggle\" class=\"mld-schedule__view-toggle\">\n <button\n v-for=\"v in (['day', 'week', 'month'] as ScheduleView[])\"\n :key=\"v\"\n type=\"button\"\n :class=\"['mld-schedule__view-btn', currentView === v ? 'mld-schedule__view-btn--active' : '']\"\n @click=\"setView(v)\"\n >\n {{ v.charAt(0).toUpperCase() + v.slice(1) }}\n </button>\n </div>\n </div>\n\n <!-- Day / Week View -->\n <template v-if=\"currentView === 'day' || currentView === 'week'\">\n <!-- Day headers -->\n <div v-if=\"currentView === 'week'\" class=\"mld-schedule__day-headers\">\n <div class=\"mld-schedule__day-header-gutter\" />\n <div\n v-for=\"(day, i) in visibleDays\"\n :key=\"i\"\n :class=\"['mld-schedule__day-header', isToday(day) ? 'mld-schedule__day-header--today' : '']\"\n >\n <slot name=\"day-header\" :date=\"day\" :isToday=\"isToday(day)\">\n <span class=\"mld-schedule__day-header-name\">\n {{ day.toLocaleDateString(locale, { weekday: 'short' }) }}\n </span>\n <span class=\"mld-schedule__day-header-number\">\n {{ day.getDate() }}\n </span>\n </slot>\n </div>\n </div>\n\n <!-- Time grid body -->\n <div ref=\"bodyRef\" class=\"mld-schedule__body\">\n <!-- Time gutter -->\n <div class=\"mld-schedule__time-gutter\">\n <div\n v-for=\"(label, i) in timeLabels\"\n :key=\"i\"\n class=\"mld-schedule__time-label\"\n >\n <slot name=\"time-label\" :label=\"label\" :index=\"i\">\n {{ i < timeLabels.length - 1 ? label : '' }}\n </slot>\n </div>\n </div>\n\n <!-- Day columns -->\n <div class=\"mld-schedule__day-columns\">\n <div\n v-for=\"(day, dayIdx) in visibleDays\"\n :key=\"dayIdx\"\n :class=\"['mld-schedule__day-column', isToday(day) ? 'mld-schedule__day-column--today' : '']\"\n >\n <!-- Slot grid -->\n <div\n v-for=\"slotIdx in totalSlots\"\n :key=\"slotIdx\"\n :class=\"['mld-schedule__slot', !readonly ? 'mld-schedule__slot--interactive' : '']\"\n @click=\"onSlotClick(day, slotIdx - 1)\"\n @pointerdown=\"onSlotPointerDown(day, slotIdx - 1, $event)\"\n />\n\n <!-- Events -->\n <div\n v-for=\"event in getEventsForDay(day)\"\n :key=\"event.id\"\n :class=\"['mld-schedule__event', getStatusClass(event.status)]\"\n :style=\"getEventStyle(event, day) || undefined\"\n @click=\"onEventClick(event, $event)\"\n @pointerdown=\"onEventPointerDown(event, dayIdx, $event)\"\n >\n <slot name=\"event\" :event=\"event\">\n <div\n v-if=\"!readonly && event.resizable !== false\"\n class=\"mld-schedule__event-resize-handle mld-schedule__event-resize-handle--top\"\n @pointerdown=\"onResizePointerDown(event, 'top', dayIdx, $event)\"\n />\n <div class=\"mld-schedule__event-title\">{{ event.title }}</div>\n <div class=\"mld-schedule__event-time\">{{ formatEventTime(event) }}</div>\n <div\n v-if=\"!readonly && event.resizable !== false\"\n class=\"mld-schedule__event-resize-handle mld-schedule__event-resize-handle--bottom\"\n @pointerdown=\"onResizePointerDown(event, 'bottom', dayIdx, $event)\"\n />\n </slot>\n </div>\n\n <!-- Blocked slots -->\n <div\n v-for=\"(blocked, bIdx) in getBlockedForDay(day)\"\n :key=\"`b-${bIdx}`\"\n class=\"mld-schedule__blocked\"\n :style=\"getBlockedStyle(blocked)\"\n >\n <span v-if=\"blocked.label\" class=\"mld-schedule__blocked-label\">\n {{ blocked.label }}\n </span>\n </div>\n\n <!-- Now indicator -->\n <div\n v-if=\"showNowIndicator && isToday(day) && getNowIndicatorStyle()\"\n class=\"mld-schedule__now-indicator\"\n :style=\"getNowIndicatorStyle()!\"\n >\n <div class=\"mld-schedule__now-dot\" />\n </div>\n\n <!-- Drag ghost -->\n <div\n v-if=\"isDragging && ghost && ghost.dayIndex === dayIdx\"\n class=\"mld-schedule__ghost\"\n :style=\"ghost.style\"\n />\n </div>\n </div>\n </div>\n </template>\n\n <!-- Month View -->\n <template v-if=\"currentView === 'month'\">\n <div class=\"mld-schedule__month-grid\">\n <!-- Weekday headers -->\n <div\n v-for=\"label in monthWeekdayLabels\"\n :key=\"label\"\n class=\"mld-schedule__month-weekday\"\n >\n {{ label }}\n </div>\n\n <!-- Day cells -->\n <div\n v-for=\"(day, i) in monthDays\"\n :key=\"i\"\n :class=\"[\n 'mld-schedule__month-cell',\n isToday(day.date) ? 'mld-schedule__month-cell--today' : '',\n !day.isCurrentMonth ? 'mld-schedule__month-cell--outside' : '',\n ]\"\n @click=\"onMonthDayClick(day.date)\"\n >\n <slot name=\"month-day\" :date=\"day.date\" :isToday=\"isToday(day.date)\" :events=\"getEventsForDate(day.date)\">\n <div class=\"mld-schedule__month-date\">{{ day.date.getDate() }}</div>\n <div\n v-for=\"event in getEventsForDate(day.date).slice(0, 3)\"\n :key=\"event.id\"\n :class=\"['mld-schedule__month-event', getMonthStatusClass(event.status)]\"\n @click.stop=\"onEventClick(event, $event)\"\n >\n {{ event.title }}\n </div>\n <div\n v-if=\"getEventsForDate(day.date).length > 3\"\n class=\"mld-schedule__month-more\"\n >\n +{{ getEventsForDate(day.date).length - 3 }} more\n </div>\n </slot>\n </div>\n </div>\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/schedule-calendar.css';\n</style>\n"],"names":["_createElementBlock","_openBlock","_createElementVNode","_toDisplayString","_Fragment","_renderList","_normalizeClass","_renderSlot","_createTextVNode","_normalizeStyle","_unref","_withModifiers"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ScheduleCalendar.vue.js","sources":["../../src/components/ScheduleCalendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch, onMounted, onUnmounted } from 'vue'\nimport type {\n ScheduleView,\n ScheduleEvent,\n ScheduleBlockedSlot,\n ScheduleEventStatus,\n} from '../types/components'\nimport { formatTime } from '../composables/useTimeUtils'\nimport { useScheduleDrag } from '../composables/useScheduleDrag'\n\ninterface Props {\n modelValue?: Date | string\n view?: ScheduleView\n events?: ScheduleEvent[]\n dayStartHour?: number\n dayEndHour?: number\n slotDuration?: number\n showNowIndicator?: boolean\n readonly?: boolean\n blockedSlots?: ScheduleBlockedSlot[]\n weekStartsOn?: 0 | 1\n showViewToggle?: boolean\n showNavigation?: boolean\n statusColors?: Record<string, string>\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n view: 'week',\n events: () => [],\n dayStartHour: 6,\n dayEndHour: 22,\n slotDuration: 30,\n showNowIndicator: true,\n readonly: false,\n blockedSlots: () => [],\n weekStartsOn: 1,\n showViewToggle: true,\n showNavigation: true,\n locale: 'en-US',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: Date]\n 'update:view': [value: ScheduleView]\n 'event-click': [event: ScheduleEvent]\n 'event-create': [context: { start: Date; end: Date }]\n 'event-update': [context: { event: ScheduleEvent; newStart: Date; newEnd: Date }]\n 'slot-click': [context: { date: Date; hour: number; minute: number }]\n 'navigate': [context: { direction: string; date: Date }]\n}>()\n\nconst SLOT_HEIGHT = 40\nconst currentView = ref<ScheduleView>(props.view)\nconst currentDate = ref<Date>(parseModelDate(props.modelValue))\nconst nowTimer = ref<ReturnType<typeof setInterval>>()\nconst now = ref(new Date())\nconst bodyRef = ref<HTMLDivElement>()\n\nwatch(() => props.view, (v) => { currentView.value = v })\nwatch(() => props.modelValue, (v) => { if (v) currentDate.value = parseModelDate(v) })\n\nconst slotHeightRef = computed(() => SLOT_HEIGHT)\nconst slotDurationRef = computed(() => props.slotDuration)\nconst dayStartRef = computed(() => props.dayStartHour)\nconst dayEndRef = computed(() => props.dayEndHour)\nconst readonlyRef = computed(() => props.readonly)\n\nconst { isDragging, ghost, startCreate, startMove, startResize } = useScheduleDrag({\n slotDuration: slotDurationRef,\n dayStartHour: dayStartRef,\n dayEndHour: dayEndRef,\n readonly: readonlyRef,\n slotHeight: slotHeightRef,\n onCreateComplete(start, end) {\n emit('event-create', { start, end })\n },\n onMoveComplete(event, newStart, newEnd) {\n emit('event-update', { event, newStart, newEnd })\n },\n onResizeComplete(event, newStart, newEnd) {\n emit('event-update', { event, newStart, newEnd })\n },\n})\n\nconst totalSlots = computed(() => {\n return ((props.dayEndHour - props.dayStartHour) * 60) / props.slotDuration\n})\n\nconst timeLabels = computed(() => {\n const labels: string[] = []\n for (let i = 0; i <= totalSlots.value; i++) {\n const totalMinutes = props.dayStartHour * 60 + i * props.slotDuration\n const hour = Math.floor(totalMinutes / 60)\n const minute = totalMinutes % 60\n labels.push(formatTime(hour, minute, '24h'))\n }\n return labels\n})\n\nconst weekDays = computed(() => {\n const days: Date[] = []\n const start = getWeekStart(currentDate.value)\n for (let i = 0; i < 7; i++) {\n const d = new Date(start)\n d.setDate(d.getDate() + i)\n days.push(d)\n }\n return days\n})\n\nconst visibleDays = computed(() => {\n if (currentView.value === 'day') return [currentDate.value]\n if (currentView.value === 'week') return weekDays.value\n return []\n})\n\nconst headerTitle = computed(() => {\n if (currentView.value === 'day') {\n return currentDate.value.toLocaleDateString(props.locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })\n }\n if (currentView.value === 'week') {\n const days = weekDays.value\n const first = days[0]\n const last = days[6]\n const sameMonth = first.getMonth() === last.getMonth()\n if (sameMonth) {\n return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.getDate()}, ${last.getFullYear()}`\n }\n return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })}, ${last.getFullYear()}`\n }\n return currentDate.value.toLocaleDateString(props.locale, { year: 'numeric', month: 'long' })\n})\n\n// Month view computations\nconst monthDays = computed(() => {\n const year = currentDate.value.getFullYear()\n const month = currentDate.value.getMonth()\n const firstDay = new Date(year, month, 1)\n const lastDay = new Date(year, month + 1, 0)\n\n const days: { date: Date; isCurrentMonth: boolean }[] = []\n\n // Adjust for weekStartsOn\n let startPadding = firstDay.getDay() - props.weekStartsOn\n if (startPadding < 0) startPadding += 7\n\n for (let i = startPadding - 1; i >= 0; i--) {\n const date = new Date(year, month, -i)\n days.push({ date, isCurrentMonth: false })\n }\n\n for (let i = 1; i <= lastDay.getDate(); i++) {\n days.push({ date: new Date(year, month, i), isCurrentMonth: true })\n }\n\n const remaining = 42 - days.length\n for (let i = 1; i <= remaining; i++) {\n days.push({ date: new Date(year, month + 1, i), isCurrentMonth: false })\n }\n\n return days\n})\n\nconst monthWeekdayLabels = computed(() => {\n const labels: string[] = []\n const base = new Date(2024, 0, props.weekStartsOn === 1 ? 1 : 7) // A known Monday or Sunday\n for (let i = 0; i < 7; i++) {\n const d = new Date(base)\n d.setDate(d.getDate() + i)\n labels.push(d.toLocaleDateString(props.locale, { weekday: 'short' }))\n }\n return labels\n})\n\nfunction parseModelDate(value?: Date | string): Date {\n if (!value) return new Date()\n if (value instanceof Date) return value\n const d = new Date(value)\n return isNaN(d.getTime()) ? new Date() : d\n}\n\nfunction getWeekStart(date: Date): Date {\n const d = new Date(date)\n const day = d.getDay()\n const diff = (day - props.weekStartsOn + 7) % 7\n d.setDate(d.getDate() - diff)\n return d\n}\n\nfunction isToday(date: Date): boolean {\n return date.toDateString() === now.value.toDateString()\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return a.toDateString() === b.toDateString()\n}\n\nfunction navigate(direction: 'prev' | 'next' | 'today') {\n const d = new Date(currentDate.value)\n if (direction === 'today') {\n currentDate.value = new Date()\n } else {\n const delta = direction === 'prev' ? -1 : 1\n if (currentView.value === 'day') d.setDate(d.getDate() + delta)\n else if (currentView.value === 'week') d.setDate(d.getDate() + delta * 7)\n else d.setMonth(d.getMonth() + delta)\n currentDate.value = d\n }\n emit('update:modelValue', currentDate.value)\n emit('navigate', { direction, date: currentDate.value })\n}\n\nfunction setView(view: ScheduleView) {\n currentView.value = view\n emit('update:view', view)\n}\n\nfunction getEventStyle(event: ScheduleEvent, dayDate: Date) {\n const eventStart = new Date(event.start)\n const eventEnd = new Date(event.end)\n if (!isSameDay(eventStart, dayDate) && !isSameDay(eventEnd, dayDate)) return null\n\n const dayStartMin = props.dayStartHour * 60\n const startMin = Math.max(eventStart.getHours() * 60 + eventStart.getMinutes(), dayStartMin) - dayStartMin\n const endMin = Math.min(eventEnd.getHours() * 60 + eventEnd.getMinutes(), props.dayEndHour * 60) - dayStartMin\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return {\n top: `${startMin * pixelsPerMin}px`,\n height: `${Math.max((endMin - startMin) * pixelsPerMin, SLOT_HEIGHT / 2)}px`,\n }\n}\n\nfunction getEventsForDay(day: Date): ScheduleEvent[] {\n return props.events.filter((e) => {\n const start = new Date(e.start)\n return isSameDay(start, day)\n })\n}\n\nfunction getEventsForDate(date: Date): ScheduleEvent[] {\n return props.events.filter((e) => {\n const start = new Date(e.start)\n return isSameDay(start, date)\n })\n}\n\nfunction getBlockedForDay(day: Date): ScheduleBlockedSlot[] {\n return props.blockedSlots.filter((b) => {\n const start = new Date(b.start)\n return isSameDay(start, day)\n })\n}\n\nfunction getBlockedStyle(slot: ScheduleBlockedSlot) {\n const start = new Date(slot.start)\n const end = new Date(slot.end)\n const dayStartMin = props.dayStartHour * 60\n const startMin = Math.max(start.getHours() * 60 + start.getMinutes(), dayStartMin) - dayStartMin\n const endMin = Math.min(end.getHours() * 60 + end.getMinutes(), props.dayEndHour * 60) - dayStartMin\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return {\n top: `${startMin * pixelsPerMin}px`,\n height: `${(endMin - startMin) * pixelsPerMin}px`,\n }\n}\n\nfunction getNowIndicatorStyle(): Record<string, string> | null {\n if (!props.showNowIndicator) return null\n const nowMin = now.value.getHours() * 60 + now.value.getMinutes()\n const dayStartMin = props.dayStartHour * 60\n const dayEndMin = props.dayEndHour * 60\n if (nowMin < dayStartMin || nowMin > dayEndMin) return null\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return { top: `${(nowMin - dayStartMin) * pixelsPerMin}px` }\n}\n\nfunction getStatusClass(status?: ScheduleEventStatus): string {\n if (!status) return 'mld-schedule__event--confirmed'\n return `mld-schedule__event--${status}`\n}\n\nfunction getMonthStatusClass(status?: ScheduleEventStatus): string {\n if (!status) return 'mld-schedule__month-event--confirmed'\n return `mld-schedule__month-event--${status}`\n}\n\nfunction onEventClick(event: ScheduleEvent, e: MouseEvent) {\n e.stopPropagation()\n emit('event-click', event)\n}\n\nfunction onSlotClick(day: Date, slotIndex: number) {\n if (props.readonly || isDragging.value) return\n const totalMinutes = props.dayStartHour * 60 + slotIndex * props.slotDuration\n const hour = Math.floor(totalMinutes / 60)\n const minute = totalMinutes % 60\n const date = new Date(day)\n date.setHours(hour, minute, 0, 0)\n emit('slot-click', { date, hour, minute })\n}\n\nfunction onSlotPointerDown(day: Date, slotIndex: number, e: PointerEvent) {\n if (props.readonly) return\n const date = new Date(day)\n const totalMinutes = props.dayStartHour * 60 + slotIndex * props.slotDuration\n date.setHours(Math.floor(totalMinutes / 60), totalMinutes % 60, 0, 0)\n const dayIndex = visibleDays.value.findIndex((d) => isSameDay(d, day))\n startCreate(date, e.clientY, dayIndex)\n}\n\nfunction onEventPointerDown(event: ScheduleEvent, dayIndex: number, e: PointerEvent) {\n e.stopPropagation()\n startMove(event, e.clientY, dayIndex)\n}\n\nfunction onResizePointerDown(event: ScheduleEvent, edge: 'top' | 'bottom', dayIndex: number, e: PointerEvent) {\n e.stopPropagation()\n startResize(event, edge, e.clientY, dayIndex)\n}\n\nfunction onMonthDayClick(date: Date) {\n currentDate.value = date\n emit('update:modelValue', date)\n setView('day')\n}\n\nfunction formatEventTime(event: ScheduleEvent): string {\n const start = new Date(event.start)\n const end = new Date(event.end)\n return `${formatTime(start.getHours(), start.getMinutes())} - ${formatTime(end.getHours(), end.getMinutes())}`\n}\n\nonMounted(() => {\n nowTimer.value = setInterval(() => { now.value = new Date() }, 60000)\n\n // Scroll to current time or 8am\n if (bodyRef.value) {\n const targetHour = now.value.getHours() >= props.dayStartHour && now.value.getHours() <= props.dayEndHour\n ? now.value.getHours()\n : 8\n const offsetMin = (targetHour - props.dayStartHour) * 60\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n bodyRef.value.scrollTop = Math.max(0, offsetMin * pixelsPerMin - 100)\n }\n})\n\nonUnmounted(() => {\n if (nowTimer.value) clearInterval(nowTimer.value)\n})\n</script>\n\n<template>\n <div class=\"mld-schedule\" :style=\"({ '--slot-height': `${SLOT_HEIGHT}px` } as Record<string, string>)\">\n <!-- Header -->\n <div v-if=\"showNavigation || showViewToggle\" class=\"mld-schedule__header\">\n <div v-if=\"showNavigation\" class=\"mld-schedule__nav\">\n <button\n type=\"button\"\n class=\"mld-schedule__nav-btn\"\n aria-label=\"Previous\"\n @click=\"navigate('prev')\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mld-schedule__nav-btn\"\n aria-label=\"Next\"\n @click=\"navigate('next')\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mld-schedule__today-btn\"\n @click=\"navigate('today')\"\n >\n Today\n </button>\n </div>\n\n <span class=\"mld-schedule__title\">{{ headerTitle }}</span>\n\n <div v-if=\"showViewToggle\" class=\"mld-schedule__view-toggle\">\n <button\n v-for=\"v in (['day', 'week', 'month'] as ScheduleView[])\"\n :key=\"v\"\n type=\"button\"\n :class=\"['mld-schedule__view-btn', currentView === v ? 'mld-schedule__view-btn--active' : '']\"\n @click=\"setView(v)\"\n >\n {{ v.charAt(0).toUpperCase() + v.slice(1) }}\n </button>\n </div>\n </div>\n\n <!-- Day / Week View -->\n <template v-if=\"currentView === 'day' || currentView === 'week'\">\n <!-- Day headers -->\n <div v-if=\"currentView === 'week'\" class=\"mld-schedule__day-headers\">\n <div class=\"mld-schedule__day-header-gutter\" />\n <div\n v-for=\"(day, i) in visibleDays\"\n :key=\"i\"\n :class=\"['mld-schedule__day-header', isToday(day) ? 'mld-schedule__day-header--today' : '']\"\n >\n <slot name=\"day-header\" :date=\"day\" :isToday=\"isToday(day)\">\n <span class=\"mld-schedule__day-header-name\">\n {{ day.toLocaleDateString(locale, { weekday: 'short' }) }}\n </span>\n <span class=\"mld-schedule__day-header-number\">\n {{ day.getDate() }}\n </span>\n </slot>\n </div>\n </div>\n\n <!-- Time grid body -->\n <div ref=\"bodyRef\" class=\"mld-schedule__body\">\n <!-- Time gutter -->\n <div class=\"mld-schedule__time-gutter\">\n <div\n v-for=\"(label, i) in timeLabels\"\n :key=\"i\"\n class=\"mld-schedule__time-label\"\n >\n <slot name=\"time-label\" :label=\"label\" :index=\"i\">\n {{ i < timeLabels.length - 1 ? label : '' }}\n </slot>\n </div>\n </div>\n\n <!-- Day columns -->\n <div class=\"mld-schedule__day-columns\">\n <div\n v-for=\"(day, dayIdx) in visibleDays\"\n :key=\"dayIdx\"\n :class=\"['mld-schedule__day-column', isToday(day) ? 'mld-schedule__day-column--today' : '']\"\n >\n <!-- Slot grid -->\n <div\n v-for=\"slotIdx in totalSlots\"\n :key=\"slotIdx\"\n :class=\"['mld-schedule__slot', !readonly ? 'mld-schedule__slot--interactive' : '']\"\n @click=\"onSlotClick(day, slotIdx - 1)\"\n @pointerdown=\"onSlotPointerDown(day, slotIdx - 1, $event)\"\n />\n\n <!-- Events -->\n <div\n v-for=\"event in getEventsForDay(day)\"\n :key=\"event.id\"\n :class=\"['mld-schedule__event', getStatusClass(event.status)]\"\n :style=\"getEventStyle(event, day) || undefined\"\n @click=\"onEventClick(event, $event)\"\n @pointerdown=\"onEventPointerDown(event, dayIdx, $event)\"\n >\n <slot name=\"event\" :event=\"event\">\n <div\n v-if=\"!readonly && event.resizable !== false\"\n class=\"mld-schedule__event-resize-handle mld-schedule__event-resize-handle--top\"\n @pointerdown=\"onResizePointerDown(event, 'top', dayIdx, $event)\"\n />\n <div class=\"mld-schedule__event-title\">{{ event.title }}</div>\n <div class=\"mld-schedule__event-time\">{{ formatEventTime(event) }}</div>\n <div\n v-if=\"!readonly && event.resizable !== false\"\n class=\"mld-schedule__event-resize-handle mld-schedule__event-resize-handle--bottom\"\n @pointerdown=\"onResizePointerDown(event, 'bottom', dayIdx, $event)\"\n />\n </slot>\n </div>\n\n <!-- Blocked slots -->\n <div\n v-for=\"(blocked, bIdx) in getBlockedForDay(day)\"\n :key=\"`b-${bIdx}`\"\n class=\"mld-schedule__blocked\"\n :style=\"getBlockedStyle(blocked)\"\n >\n <span v-if=\"blocked.label\" class=\"mld-schedule__blocked-label\">\n {{ blocked.label }}\n </span>\n </div>\n\n <!-- Now indicator -->\n <div\n v-if=\"showNowIndicator && isToday(day) && getNowIndicatorStyle()\"\n class=\"mld-schedule__now-indicator\"\n :style=\"getNowIndicatorStyle()!\"\n >\n <div class=\"mld-schedule__now-dot\" />\n </div>\n\n <!-- Drag ghost -->\n <div\n v-if=\"isDragging && ghost && ghost.dayIndex === dayIdx\"\n class=\"mld-schedule__ghost\"\n :style=\"ghost.style\"\n />\n </div>\n </div>\n </div>\n </template>\n\n <!-- Month View -->\n <template v-if=\"currentView === 'month'\">\n <div class=\"mld-schedule__month-grid\">\n <!-- Weekday headers -->\n <div\n v-for=\"label in monthWeekdayLabels\"\n :key=\"label\"\n class=\"mld-schedule__month-weekday\"\n >\n {{ label }}\n </div>\n\n <!-- Day cells -->\n <div\n v-for=\"(day, i) in monthDays\"\n :key=\"i\"\n :class=\"[\n 'mld-schedule__month-cell',\n isToday(day.date) ? 'mld-schedule__month-cell--today' : '',\n !day.isCurrentMonth ? 'mld-schedule__month-cell--outside' : '',\n ]\"\n @click=\"onMonthDayClick(day.date)\"\n >\n <slot name=\"month-day\" :date=\"day.date\" :isToday=\"isToday(day.date)\" :events=\"getEventsForDate(day.date)\">\n <div class=\"mld-schedule__month-date\">{{ day.date.getDate() }}</div>\n <div\n v-for=\"event in getEventsForDate(day.date).slice(0, 3)\"\n :key=\"event.id\"\n :class=\"['mld-schedule__month-event', getMonthStatusClass(event.status)]\"\n @click.stop=\"onEventClick(event, $event)\"\n >\n {{ event.title }}\n </div>\n <div\n v-if=\"getEventsForDate(day.date).length > 3\"\n class=\"mld-schedule__month-more\"\n >\n +{{ getEventsForDate(day.date).length - 3 }} more\n </div>\n </slot>\n </div>\n </div>\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/schedule-calendar.css';\n</style>\n"],"names":["_createElementBlock","_openBlock","_createElementVNode","_toDisplayString","_Fragment","_renderList","_normalizeClass","_renderSlot","_createTextVNode","_normalizeStyle","_unref","_withModifiers"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScientificNumber.vue.js","sources":["../../src/components/ScientificNumber.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\
|
|
1
|
+
{"version":3,"file":"ScientificNumber.vue.js","sources":["../../src/components/ScientificNumber.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport type { NumberNotation } from '../types'\n\ninterface Props {\n value: number\n precision?: number\n notation?: NumberNotation\n unit?: string\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n precision: 3,\n notation: 'auto',\n copyable: false,\n})\n\nconst copied = ref(false)\n\nconst SUPERSCRIPT_DIGITS: Record<string, string> = {\n '0': '\\u2070',\n '1': '\\u00B9',\n '2': '\\u00B2',\n '3': '\\u00B3',\n '4': '\\u2074',\n '5': '\\u2075',\n '6': '\\u2076',\n '7': '\\u2077',\n '8': '\\u2078',\n '9': '\\u2079',\n '-': '\\u207B',\n}\n\nconst SI_SUFFIXES: [number, string][] = [\n [1e12, 'T'],\n [1e9, 'G'],\n [1e6, 'M'],\n [1e3, 'k'],\n [1e-3, 'm'],\n [1e-6, '\\u00B5'],\n [1e-9, 'n'],\n [1e-12, 'p'],\n]\n\nfunction toSuperscript(exp: number): string {\n return String(exp)\n .split('')\n .map(ch => SUPERSCRIPT_DIGITS[ch] ?? ch)\n .join('')\n}\n\nfunction roundToPrecision(n: number, digits: number): string {\n return n.toFixed(digits).replace(/\\.?0+$/, '') || '0'\n}\n\ninterface FormattedNumber {\n type: 'plain' | 'scientific' | 'compact'\n mantissa?: string\n exponent?: number\n exponentStr?: string\n plain?: string\n suffix?: string\n}\n\nconst formatted = computed<FormattedNumber>(() => {\n const v = props.value\n\n if (!Number.isFinite(v)) return { type: 'plain', plain: '' }\n if (v === 0) return { type: 'plain', plain: '0' }\n\n const absV = Math.abs(v)\n\n if (props.notation === 'compact') {\n for (const [threshold, suffix] of SI_SUFFIXES) {\n if (absV >= threshold * 0.999) {\n return {\n type: 'compact',\n plain: roundToPrecision(v / threshold, props.precision),\n suffix,\n }\n }\n }\n return { type: 'plain', plain: roundToPrecision(v, props.precision) }\n }\n\n const useScientific =\n props.notation === 'scientific' ||\n props.notation === 'engineering' ||\n (props.notation === 'auto' && (absV < 0.01 || absV >= 10000))\n\n if (!useScientific) {\n return { type: 'plain', plain: roundToPrecision(v, props.precision) }\n }\n\n let exp = Math.floor(Math.log10(absV))\n\n if (props.notation === 'engineering') {\n exp = Math.floor(exp / 3) * 3\n }\n\n const mantissa = v / Math.pow(10, exp)\n const mantissaStr = roundToPrecision(mantissa, props.precision)\n\n return {\n type: 'scientific',\n mantissa: mantissaStr,\n exponent: exp,\n exponentStr: toSuperscript(exp),\n }\n})\n\nconst specialDisplay = computed(() => {\n if (Number.isNaN(props.value)) return 'NaN'\n if (props.value === Infinity) return '\\u221E'\n if (props.value === -Infinity) return '-\\u221E'\n return null\n})\n\nconst plainTextValue = computed(() => {\n if (specialDisplay.value) return specialDisplay.value\n const f = formatted.value\n let text: string\n if (f.type === 'scientific') {\n text = `${f.mantissa}e${f.exponent}`\n } else if (f.type === 'compact') {\n text = `${f.plain}${f.suffix ?? ''}`\n } else {\n text = f.plain ?? '0'\n }\n if (props.unit) text += ` ${props.unit}`\n return text\n})\n\nasync function copyToClipboard() {\n try {\n await navigator.clipboard.writeText(plainTextValue.value)\n copied.value = true\n setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // clipboard not available\n }\n}\n</script>\n\n<template>\n <span class=\"mld-sci-number\">\n <!-- Special values: NaN, Infinity -->\n <template v-if=\"specialDisplay\">\n <span class=\"mld-sci-number__plain\">{{ specialDisplay }}</span>\n </template>\n\n <!-- Scientific / Engineering notation -->\n <template v-else-if=\"formatted.type === 'scientific'\">\n <span class=\"mld-sci-number__mantissa\">{{ formatted.mantissa }}</span>\n <span class=\"mld-sci-number__times\">×</span>\n <span class=\"mld-sci-number__base\">10</span>\n <span class=\"mld-sci-number__exponent\">{{ formatted.exponentStr }}</span>\n </template>\n\n <!-- Compact notation with SI suffix -->\n <template v-else-if=\"formatted.type === 'compact'\">\n <span class=\"mld-sci-number__plain\">{{ formatted.plain }}</span>\n <span v-if=\"formatted.suffix\" class=\"mld-sci-number__suffix\">{{ formatted.suffix }}</span>\n </template>\n\n <!-- Plain number -->\n <template v-else>\n <span class=\"mld-sci-number__plain\">{{ formatted.plain }}</span>\n </template>\n\n <!-- Unit -->\n <span v-if=\"unit\" class=\"mld-sci-number__unit\">{{ unit }}</span>\n\n <!-- Copy button -->\n <button\n v-if=\"copyable\"\n :class=\"['mld-sci-number__copy-btn', { 'mld-sci-number__copy-btn--copied': copied }]\"\n type=\"button\"\n :title=\"copied ? 'Copied!' : 'Copy value'\"\n @click.stop=\"copyToClipboard\"\n >\n <svg v-if=\"!copied\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n <svg v-else width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </button>\n </span>\n</template>\n\n<style>\n@import '../styles/components/scientific-number.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_Fragment","_createElementVNode"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsModal.vue.js","sources":["../../src/components/SettingsModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport { useSettingsStore, colorPalettes } from '../stores/settings'\nimport type { ThemeMode, ColorPalette, TableDensity } from '../types'\n\
|
|
1
|
+
{"version":3,"file":"SettingsModal.vue.js","sources":["../../src/components/SettingsModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport { useSettingsStore, colorPalettes } from '../stores/settings'\nimport type { ThemeMode, ColorPalette, TableDensity, SettingsTab } from '../types'\n\ninterface Props {\n modelValue: boolean\n title?: string\n tabs?: SettingsTab[]\n showAppearance?: boolean\n size?: 'md' | 'lg' | 'xl'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Settings',\n tabs: () => [],\n showAppearance: true,\n size: 'lg',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n close: []\n}>()\n\nconst settings = useSettingsStore()\n\nconst allTabs = computed<SettingsTab[]>(() =>\n props.showAppearance\n ? [...props.tabs, { id: 'appearance', label: 'Appearance' }]\n : props.tabs\n)\n\nconst activeTab = ref(allTabs.value[0]?.id || 'appearance')\n\nconst themeOptions: { value: ThemeMode; label: string }[] = [\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n { value: 'system', label: 'System' },\n]\n\nconst densityOptions: { value: TableDensity; label: string }[] = [\n { value: 'compact', label: 'Compact' },\n { value: 'normal', label: 'Normal' },\n { value: 'comfortable', label: 'Comfortable' },\n]\n\nfunction handleClose() {\n emit('update:modelValue', false)\n emit('close')\n}\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleClose\"\n >\n <div class=\"mld-settings-modal\">\n <!-- Tabs -->\n <div v-if=\"allTabs.length > 1\" class=\"mld-settings-modal__tabs\">\n <button\n v-for=\"tab in allTabs\"\n :key=\"tab.id\"\n type=\"button\"\n :class=\"['mld-settings-modal__tab', { 'mld-settings-modal__tab--active': activeTab === tab.id }]\"\n @click=\"activeTab = tab.id\"\n >\n {{ tab.label }}\n </button>\n </div>\n\n <!-- Custom tab content -->\n <div class=\"mld-settings-modal__content\">\n <template v-for=\"tab in tabs\" :key=\"tab.id\">\n <div v-show=\"activeTab === tab.id\">\n <slot :name=\"`tab-${tab.id}`\" />\n </div>\n </template>\n\n <!-- Built-in appearance tab -->\n <div v-if=\"showAppearance\" v-show=\"activeTab === 'appearance'\">\n <!-- Theme -->\n <div class=\"mld-settings-modal__section\">\n <div class=\"mld-settings-modal__section-label\">Theme</div>\n <div class=\"mld-settings-modal__option-group\">\n <button\n v-for=\"opt in themeOptions\"\n :key=\"opt.value\"\n type=\"button\"\n :class=\"['mld-settings-modal__option-btn', { 'mld-settings-modal__option-btn--active': settings.theme === opt.value }]\"\n @click=\"settings.theme = opt.value\"\n >\n {{ opt.label }}\n </button>\n </div>\n </div>\n\n <!-- Color palette -->\n <div class=\"mld-settings-modal__section\">\n <div class=\"mld-settings-modal__section-label\">Color Palette</div>\n <div class=\"mld-settings-modal__option-group\">\n <button\n v-for=\"(palette, key) in colorPalettes\"\n :key=\"key\"\n type=\"button\"\n :class=\"['mld-settings-modal__option-btn', { 'mld-settings-modal__option-btn--active': settings.colorPalette === key }]\"\n @click=\"settings.colorPalette = key as ColorPalette\"\n >\n {{ palette.name }}\n </button>\n </div>\n </div>\n\n <!-- Table density -->\n <div class=\"mld-settings-modal__section\">\n <div class=\"mld-settings-modal__section-label\">Table Density</div>\n <div class=\"mld-settings-modal__option-group\">\n <button\n v-for=\"opt in densityOptions\"\n :key=\"opt.value\"\n type=\"button\"\n :class=\"['mld-settings-modal__option-btn', { 'mld-settings-modal__option-btn--active': settings.tableDensity === opt.value }]\"\n @click=\"settings.tableDensity = opt.value\"\n >\n {{ opt.label }}\n </button>\n </div>\n <p class=\"mld-settings-modal__note\">Adjusts row height in data tables.</p>\n </div>\n\n <slot name=\"appearance\" />\n </div>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/settings-modal.css';\n</style>\n"],"names":["_createBlock","BaseModal","_createElementVNode","_openBlock","_createElementBlock","_Fragment","_renderList","_normalizeClass","_toDisplayString","_renderSlot","_unref"],"mappings":"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"}
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
id: string;
|
|
3
|
-
label: string;
|
|
4
|
-
description?: string;
|
|
5
|
-
icon?: string;
|
|
6
|
-
optional?: boolean;
|
|
7
|
-
}
|
|
8
|
-
type WizardStepState = 'pending' | 'active' | 'completed' | 'disabled';
|
|
1
|
+
import { WizardStep, WizardStepState } from '../types';
|
|
9
2
|
interface Props {
|
|
10
3
|
steps: WizardStep[];
|
|
11
4
|
modelValue?: number;
|