@morscherlab/mld-sdk 0.6.0 → 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.
Files changed (57) hide show
  1. package/dist/components/AuditTrail.vue.d.ts +1 -10
  2. package/dist/components/AuditTrail.vue.js.map +1 -1
  3. package/dist/components/BatchProgressList.vue.d.ts +1 -17
  4. package/dist/components/BatchProgressList.vue.js.map +1 -1
  5. package/dist/components/Breadcrumb.vue.d.ts +1 -5
  6. package/dist/components/Breadcrumb.vue.js.map +1 -1
  7. package/dist/components/DateTimePicker.vue.js +0 -1
  8. package/dist/components/DateTimePicker.vue.js.map +1 -1
  9. package/dist/components/MoleculeInput.vue.d.ts +1 -4
  10. package/dist/components/MoleculeInput.vue.js.map +1 -1
  11. package/dist/components/RackEditor.vue.js +2 -2
  12. package/dist/components/RackEditor.vue.js.map +1 -1
  13. package/dist/components/ReagentList.vue.d.ts +1 -15
  14. package/dist/components/ReagentList.vue.js.map +1 -1
  15. package/dist/components/SampleHierarchyTree.vue.d.ts +1 -12
  16. package/dist/components/SampleHierarchyTree.vue.js.map +1 -1
  17. package/dist/components/ScheduleCalendar.vue.js.map +1 -1
  18. package/dist/components/ScientificNumber.vue.d.ts +1 -1
  19. package/dist/components/ScientificNumber.vue.js.map +1 -1
  20. package/dist/components/SettingsModal.vue.d.ts +1 -5
  21. package/dist/components/SettingsModal.vue.js.map +1 -1
  22. package/dist/components/StepWizard.vue.d.ts +1 -8
  23. package/dist/components/StepWizard.vue.js.map +1 -1
  24. package/dist/components/UnitInput.vue.d.ts +1 -6
  25. package/dist/components/UnitInput.vue.js.map +1 -1
  26. package/dist/components/WellPlate.vue.d.ts +5 -1
  27. package/dist/components/WellPlate.vue.js +219 -27
  28. package/dist/components/WellPlate.vue.js.map +1 -1
  29. package/dist/styles.css +113 -88
  30. package/dist/types/components.d.ts +12 -0
  31. package/dist/types/index.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/components/AuditTrail.vue +1 -12
  34. package/src/components/BatchProgressList.vue +1 -20
  35. package/src/components/Breadcrumb.vue +1 -5
  36. package/src/components/DateTimePicker.vue +1 -1
  37. package/src/components/MoleculeInput.story.vue +1 -1
  38. package/src/components/MoleculeInput.vue +1 -5
  39. package/src/components/RackEditor.vue +2 -2
  40. package/src/components/ReagentList.story.vue +1 -1
  41. package/src/components/ReagentList.vue +1 -26
  42. package/src/components/SampleHierarchyTree.story.vue +1 -1
  43. package/src/components/SampleHierarchyTree.vue +1 -25
  44. package/src/components/ScheduleCalendar.vue +1 -1
  45. package/src/components/ScientificNumber.story.vue +1 -2
  46. package/src/components/ScientificNumber.vue +1 -2
  47. package/src/components/SettingsModal.vue +1 -7
  48. package/src/components/StepWizard.vue +1 -10
  49. package/src/components/UnitInput.vue +1 -7
  50. package/src/components/WellPlate.story.vue +85 -1
  51. package/src/components/WellPlate.vue +194 -9
  52. package/src/styles/components/button.css +2 -0
  53. package/src/styles/components/datetime-picker.css +4 -0
  54. package/src/styles/components/rack-editor.css +6 -0
  55. package/src/styles/components/well-plate.css +50 -57
  56. package/src/types/components.ts +16 -0
  57. package/src/types/index.ts +13 -0
@@ -1 +1 @@
1
- {"version":3,"file":"WellPlate.vue.js","sources":["../../src/components/WellPlate.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted } from 'vue'\nimport type { WellPlateFormat, WellPlateSelectionMode, WellPlateSize, Well, HeatmapConfig, WellShape, WellEditField, WellEditData, WellLegendItem } from '../types'\nimport WellEditPopup from './WellEditPopup.vue'\n\ninterface Props {\n modelValue?: string[]\n format?: WellPlateFormat\n wells?: Record<string, Partial<Well>>\n selectionMode?: WellPlateSelectionMode\n showLabels?: boolean\n showWellIds?: boolean\n showSampleTypeIndicator?: boolean\n heatmap?: HeatmapConfig\n sampleColors?: Record<string, string>\n zoom?: number\n disabled?: boolean\n readonly?: boolean\n size?: WellPlateSize\n wellShape?: WellShape\n // New editing props\n showWellLabels?: boolean\n showBadges?: boolean\n editable?: boolean\n editFields?: WellEditField[]\n defaultInjectionVolume?: number\n showLegend?: boolean\n legendItems?: WellLegendItem[]\n}\n\n// Drag state for moving wells\nconst dragSourceWell = ref<string | null>(null)\nconst dragTargetWell = ref<string | null>(null)\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n format: 96,\n wells: () => ({}),\n selectionMode: 'multiple',\n showLabels: true,\n showWellIds: false,\n showSampleTypeIndicator: false,\n heatmap: () => ({ enabled: false }),\n sampleColors: () => ({}),\n zoom: 1,\n disabled: false,\n readonly: false,\n size: 'md',\n wellShape: 'rounded',\n // New props default to off for backward compatibility\n showWellLabels: false,\n showBadges: false,\n editable: false,\n editFields: () => ['label', 'sampleType', 'injectionVolume', 'injectionCount', 'customMethod'],\n defaultInjectionVolume: 5,\n showLegend: false,\n legendItems: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [wellIds: string[]]\n 'well-click': [wellId: string, event: MouseEvent]\n 'well-hover': [wellId: string | null, event?: MouseEvent]\n 'selection-change': [wellIds: string[]]\n 'context-menu': [wellId: string, event: MouseEvent]\n 'well-move': [sourceWellId: string, targetWellId: string]\n // New editing emits\n 'well-edit': [wellId: string, data: WellEditData]\n 'well-clear': [wellId: string]\n}>()\n\nconst plateRef = ref<HTMLElement | null>(null)\nconst tableRef = ref<HTMLElement | null>(null)\nconst isDragging = ref(false)\nconst dragStart = ref<{ row: number; col: number } | null>(null)\nconst dragEnd = ref<{ row: number; col: number } | null>(null)\nconst hoveredWell = ref<string | null>(null)\n\n// Edit popup state\nconst editingWellId = ref<string | null>(null)\nconst editPopupPosition = ref({ x: 0, y: 0 })\n\nconst PLATE_CONFIGS: Record<WellPlateFormat, { rows: number; cols: number }> = {\n 6: { rows: 2, cols: 3 },\n 12: { rows: 3, cols: 4 },\n 24: { rows: 4, cols: 6 },\n 48: { rows: 6, cols: 8 },\n 54: { rows: 6, cols: 9 },\n 96: { rows: 8, cols: 12 },\n 384: { rows: 16, cols: 24 },\n}\n\nconst plateConfig = computed(() => PLATE_CONFIGS[props.format])\n\nconst rowLabels = computed(() =>\n Array.from({ length: plateConfig.value.rows }, (_, i) => String.fromCharCode(65 + i))\n)\n\nconst colLabels = computed(() =>\n Array.from({ length: plateConfig.value.cols }, (_, i) => i + 1)\n)\n\nconst wellGrid = computed(() => {\n const grid: Well[][] = []\n for (let row = 0; row < plateConfig.value.rows; row++) {\n const rowWells: Well[] = []\n for (let col = 0; col < plateConfig.value.cols; col++) {\n const id = `${rowLabels.value[row]}${col + 1}`\n const wellData = props.wells[id] || {}\n rowWells.push({\n id,\n row,\n col,\n state: wellData.state || 'empty',\n sampleType: wellData.sampleType,\n value: wellData.value,\n metadata: wellData.metadata,\n })\n }\n grid.push(rowWells)\n }\n return grid\n})\n\nconst selectedWellSet = computed(() => new Set(props.modelValue))\n\nconst dragSelectedWells = computed(() => {\n if (!isDragging.value || !dragStart.value || !dragEnd.value) return new Set<string>()\n\n const minRow = Math.min(dragStart.value.row, dragEnd.value.row)\n const maxRow = Math.max(dragStart.value.row, dragEnd.value.row)\n const minCol = Math.min(dragStart.value.col, dragEnd.value.col)\n const maxCol = Math.max(dragStart.value.col, dragEnd.value.col)\n\n const wells = new Set<string>()\n for (let row = minRow; row <= maxRow; row++) {\n for (let col = minCol; col <= maxCol; col++) {\n const id = `${rowLabels.value[row]}${col + 1}`\n wells.add(id)\n }\n }\n return wells\n})\n\n// Size presets with pixel values for reliable sizing\nconst sizeConfig = computed(() => {\n const sizes = {\n sm: { cellWidth: '40px', cellHeight: '40px', headerWidth: '32px', headerHeight: '32px', fontSize: '0.625rem', gap: '2px' },\n md: { cellWidth: '56px', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '3px' },\n lg: { cellWidth: '80px', cellHeight: '40px', headerWidth: '40px', headerHeight: '32px', fontSize: '0.875rem', gap: '4px' },\n xl: { cellWidth: '96px', cellHeight: '48px', headerWidth: '48px', headerHeight: '40px', fontSize: '1rem', gap: '4px' },\n fill: { cellWidth: '100%', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '2px' },\n }\n return sizes[props.size]\n})\n\nconst isFillMode = computed(() => props.size === 'fill')\n\n// Default legend items\nconst defaultLegendItems: WellLegendItem[] = [\n { type: 'sample', label: 'Sample', color: '#10b981' },\n { type: 'blank', label: 'Blank', color: '#f97316' },\n { type: 'qc', label: 'QC', color: '#8b5cf6' },\n]\n\nconst activeLegendItems = computed(() => props.legendItems ?? defaultLegendItems)\n\n// Sample type colors (matching MSExpDesigner)\nconst defaultSampleTypeColors: Record<string, { bg: string; border: string }> = {\n sample: { bg: 'rgba(16, 185, 129, 0.15)', border: 'rgba(16, 185, 129, 0.4)' },\n control: { bg: 'rgba(59, 130, 246, 0.15)', border: 'rgba(59, 130, 246, 0.4)' },\n blank: { bg: 'rgba(249, 115, 22, 0.15)', border: 'rgba(249, 115, 22, 0.4)' },\n qc: { bg: 'rgba(139, 92, 246, 0.15)', border: 'rgba(139, 92, 246, 0.4)' },\n}\n\nconst heatmapColors: Record<string, string[]> = {\n viridis: ['#440154', '#482878', '#3e4989', '#31688e', '#26828e', '#1f9e89', '#35b779', '#6ece58', '#b5de2b', '#fde725'],\n plasma: ['#0d0887', '#46039f', '#7201a8', '#9c179e', '#bd3786', '#d8576b', '#ed7953', '#fb9f3a', '#fdca26', '#f0f921'],\n turbo: ['#30123b', '#4145ab', '#4675ed', '#39a2fc', '#1bcfd4', '#24e79e', '#71f05f', '#c1f034', '#f1c83c', '#f99538', '#e45a31', '#ba2512', '#7a0403'],\n}\n\nfunction getHeatmapColor(value: number | undefined): string | null {\n if (!props.heatmap?.enabled || value === undefined) return null\n\n const min = props.heatmap.min ?? 0\n const max = props.heatmap.max ?? 1\n const normalized = Math.max(0, Math.min(1, (value - min) / (max - min)))\n\n const colors = props.heatmap.colorScale === 'custom' && props.heatmap.customColors?.length\n ? props.heatmap.customColors\n : heatmapColors[props.heatmap.colorScale || 'viridis']\n\n const index = Math.min(Math.floor(normalized * (colors.length - 1)), colors.length - 1)\n return colors[index]\n}\n\nfunction getWellClasses(well: Well): string[] {\n const isWellSelected = isSelected(well.id)\n const isDragOver = dragSelectedWells.value.has(well.id)\n const isHovered = hoveredWell.value === well.id\n const isDisabled = props.disabled || well.state === 'disabled'\n const isDragSource = dragSourceWell.value === well.id\n const isDragTarget = dragTargetWell.value === well.id\n\n const classes = [\n 'mld-well-plate__well',\n props.wellShape === 'circle' ? 'mld-well-plate__well--circle' : 'mld-well-plate__well--rounded',\n ]\n\n if (props.selectionMode === 'drag' && well.sampleType) {\n classes.push('mld-well-plate__well--draggable')\n }\n\n if (isDragSource) classes.push('mld-well-plate__well--drag-source')\n else if (isDragTarget) classes.push('mld-well-plate__well--drag-target')\n else if (isWellSelected) classes.push('mld-well-plate__well--selected')\n else if (isDragOver) classes.push('mld-well-plate__well--drag-over')\n else if (isHovered && !props.readonly) classes.push('mld-well-plate__well--hovered')\n\n if (isDisabled) classes.push('mld-well-plate__well--disabled')\n if (well.state === 'filled') classes.push('mld-well-plate__well--filled')\n\n return classes\n}\n\nfunction getWellStyle(well: Well): Record<string, string> {\n const heatmapColor = getHeatmapColor(well.value)\n if (heatmapColor) {\n return { backgroundColor: heatmapColor, border: '1px solid transparent' }\n }\n\n if (well.sampleType && props.sampleColors[well.sampleType]) {\n const color = props.sampleColors[well.sampleType]\n return {\n backgroundColor: `${color}26`,\n border: `1px solid ${color}66`,\n }\n }\n\n if (well.sampleType && defaultSampleTypeColors[well.sampleType]) {\n const colors = defaultSampleTypeColors[well.sampleType]\n return {\n backgroundColor: colors.bg,\n border: `1px solid ${colors.border}`,\n }\n }\n\n const borderStyle = well.state === 'filled' ? 'solid' : 'dashed'\n return {\n backgroundColor: 'var(--bg-tertiary)',\n border: `1px ${borderStyle} var(--border-color)`,\n }\n}\n\nfunction getSampleTypeIndicator(well: Well): string | null {\n if (!props.showSampleTypeIndicator || !well.sampleType) return null\n const typeMap: Record<string, string> = {\n sample: 'S',\n control: 'C',\n blank: 'B',\n qc: 'Q',\n }\n return typeMap[well.sampleType] || well.sampleType.charAt(0).toUpperCase()\n}\n\nfunction getWellLabel(well: Well): string | undefined {\n if (!props.showWellLabels) return undefined\n return well.metadata?.label as string | undefined\n}\n\nfunction getWellBadge(well: Well): { text: string; color: string } | null {\n if (!props.showBadges || !well.metadata) return null\n const count = well.metadata.injectionCount as number | undefined\n if (count && count > 1) {\n return { text: String(count), color: '#6366f1' }\n }\n if (well.metadata.customMethod) {\n return { text: '+', color: '#ec4899' }\n }\n return null\n}\n\nfunction isSelected(wellId: string): boolean {\n return selectedWellSet.value.has(wellId) || dragSelectedWells.value.has(wellId)\n}\n\nfunction handleWellClick(well: Well, event: MouseEvent) {\n if (props.disabled || props.readonly) return\n\n emit('well-click', well.id, event)\n\n // When editable, open popup instead of modifying selection\n if (props.editable) {\n openEditPopup(well.id, event)\n return\n }\n\n if (props.selectionMode === 'none') return\n\n const isCurrentlySelected = selectedWellSet.value.has(well.id)\n const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey\n\n let newSelection: string[]\n if (props.selectionMode === 'single') {\n newSelection = isCurrentlySelected ? [] : [well.id]\n } else if (isMultiSelect) {\n newSelection = isCurrentlySelected\n ? props.modelValue.filter(id => id !== well.id)\n : [...props.modelValue, well.id]\n } else {\n newSelection = isCurrentlySelected && props.modelValue.length === 1 ? [] : [well.id]\n }\n\n emit('update:modelValue', newSelection)\n emit('selection-change', newSelection)\n}\n\nfunction openEditPopup(wellId: string, event: MouseEvent) {\n const target = event.currentTarget as HTMLElement\n const rect = target.getBoundingClientRect()\n editPopupPosition.value = {\n x: rect.right + 8,\n y: rect.top,\n }\n editingWellId.value = wellId\n}\n\nfunction handleEditSave(data: WellEditData) {\n emit('well-edit', data.wellId, data)\n editingWellId.value = null\n}\n\nfunction handleEditClear() {\n if (editingWellId.value) {\n emit('well-clear', editingWellId.value)\n }\n editingWellId.value = null\n}\n\nfunction handleEditClose() {\n editingWellId.value = null\n}\n\nfunction handleWellMouseDown(well: Well, event: MouseEvent) {\n if (props.disabled || props.readonly || props.selectionMode !== 'rectangle') return\n if (props.editable) return\n if (event.button !== 0) return\n\n isDragging.value = true\n dragStart.value = { row: well.row, col: well.col }\n dragEnd.value = { row: well.row, col: well.col }\n}\n\nfunction handleWellMouseEnter(well: Well, event: MouseEvent) {\n hoveredWell.value = well.id\n emit('well-hover', well.id, event)\n\n if (isDragging.value && props.selectionMode === 'rectangle') {\n dragEnd.value = { row: well.row, col: well.col }\n }\n}\n\nfunction handleWellMouseLeave() {\n hoveredWell.value = null\n emit('well-hover', null)\n}\n\nfunction handleMouseUp() {\n if (!isDragging.value || props.selectionMode !== 'rectangle') return\n\n const newSelection = Array.from(dragSelectedWells.value)\n isDragging.value = false\n dragStart.value = null\n dragEnd.value = null\n\n if (newSelection.length > 0) {\n emit('update:modelValue', newSelection)\n emit('selection-change', newSelection)\n }\n}\n\nfunction handleContextMenu(well: Well, event: MouseEvent) {\n event.preventDefault()\n emit('context-menu', well.id, event)\n}\n\n// Drag handlers for moving well contents\nfunction handleDragStart(well: Well, event: DragEvent) {\n if (props.disabled || props.readonly || props.selectionMode !== 'drag') return\n if (!well.sampleType) return\n\n dragSourceWell.value = well.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', well.id)\n }\n}\n\nfunction handleDragOver(well: Well, event: DragEvent) {\n if (props.selectionMode !== 'drag' || !dragSourceWell.value) return\n event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move'\n }\n dragTargetWell.value = well.id\n}\n\nfunction handleDragLeave() {\n dragTargetWell.value = null\n}\n\nfunction handleDrop(well: Well, event: DragEvent) {\n event.preventDefault()\n if (props.selectionMode !== 'drag' || !dragSourceWell.value) return\n\n const sourceId = dragSourceWell.value\n const targetId = well.id\n\n if (sourceId !== targetId) {\n emit('well-move', sourceId, targetId)\n }\n\n dragSourceWell.value = null\n dragTargetWell.value = null\n}\n\nfunction handleDragEnd() {\n dragSourceWell.value = null\n dragTargetWell.value = null\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n if (props.disabled || props.readonly) return\n\n if (event.key === 'Escape') {\n if (editingWellId.value) {\n editingWellId.value = null\n return\n }\n emit('update:modelValue', [])\n emit('selection-change', [])\n }\n\n if ((event.key === 'a' || event.key === 'A') && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n const allWells = wellGrid.value.flat().map(w => w.id)\n emit('update:modelValue', allWells)\n emit('selection-change', allWells)\n }\n}\n\nonMounted(() => {\n document.addEventListener('mouseup', handleMouseUp)\n document.addEventListener('keydown', handleKeyDown)\n})\n\nonUnmounted(() => {\n document.removeEventListener('mouseup', handleMouseUp)\n document.removeEventListener('keydown', handleKeyDown)\n})\n\nconst containerStyle = computed(() =>\n isFillMode.value ? {} : {\n transform: `scale(${props.zoom})`,\n transformOrigin: 'top left',\n }\n)\n\nconst tableStyle = computed(() => ({\n borderCollapse: 'separate' as const,\n borderSpacing: sizeConfig.value.gap,\n ...(isFillMode.value ? { width: '100%', tableLayout: 'fixed' as const } : {}),\n}))\n</script>\n\n<template>\n <div\n ref=\"plateRef\"\n :class=\"['mld-well-plate', isFillMode ? 'mld-well-plate--fill' : 'mld-well-plate--inline']\"\n :style=\"containerStyle\"\n >\n <div class=\"mld-well-plate__scroll\">\n <div class=\"mld-well-plate__content\">\n <table\n ref=\"tableRef\"\n class=\"mld-well-plate__table\"\n role=\"grid\"\n :aria-label=\"`${props.format}-well plate`\"\n :style=\"tableStyle\"\n >\n <!-- Column headers -->\n <thead v-if=\"props.showLabels\">\n <tr>\n <th :style=\"{ width: sizeConfig.headerWidth, height: sizeConfig.headerHeight }\"></th>\n <th\n v-for=\"col in colLabels\"\n :key=\"col\"\n class=\"mld-well-plate__col-header\"\n :style=\"{ height: sizeConfig.headerHeight, fontSize: sizeConfig.fontSize }\"\n >\n {{ col }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(row, rowIndex) in wellGrid\" :key=\"rowIndex\" role=\"row\">\n <!-- Row header -->\n <td\n v-if=\"props.showLabels\"\n class=\"mld-well-plate__row-header\"\n :style=\"{ width: sizeConfig.headerWidth, height: sizeConfig.cellHeight, minWidth: sizeConfig.headerWidth, minHeight: sizeConfig.cellHeight, fontSize: sizeConfig.fontSize }\"\n >\n {{ rowLabels[rowIndex] }}\n </td>\n\n <!-- Wells -->\n <td v-for=\"well in row\" :key=\"well.id\" class=\"mld-well-plate__cell\">\n <div\n role=\"gridcell\"\n tabindex=\"0\"\n :aria-label=\"`Well ${well.id}${well.sampleType ? `, Sample type: ${well.sampleType}` : ''}${well.value !== undefined ? `, Value: ${well.value}` : ''}`\"\n :aria-selected=\"isSelected(well.id)\"\n :aria-disabled=\"props.disabled || well.state === 'disabled'\"\n :draggable=\"props.selectionMode === 'drag' && !!well.sampleType\"\n :class=\"getWellClasses(well)\"\n :style=\"{\n width: isFillMode ? '100%' : sizeConfig.cellWidth,\n height: sizeConfig.cellHeight,\n minWidth: isFillMode ? '0' : sizeConfig.cellWidth,\n minHeight: sizeConfig.cellHeight,\n boxSizing: 'border-box',\n fontSize: sizeConfig.fontSize,\n ...getWellStyle(well),\n }\"\n :title=\"`${well.id}${well.sampleType ? `: ${well.sampleType}` : ''}${getWellLabel(well) ? ` - ${getWellLabel(well)}` : ''}`\"\n @click=\"handleWellClick(well, $event)\"\n @mousedown=\"handleWellMouseDown(well, $event)\"\n @mouseenter=\"handleWellMouseEnter(well, $event)\"\n @mouseleave=\"handleWellMouseLeave\"\n @contextmenu=\"handleContextMenu(well, $event)\"\n @dragstart=\"handleDragStart(well, $event)\"\n @dragover.prevent=\"handleDragOver(well, $event)\"\n @dragleave=\"handleDragLeave\"\n @drop.prevent=\"handleDrop(well, $event)\"\n @dragend=\"handleDragEnd\"\n @keydown.enter=\"handleWellClick(well, $event as unknown as MouseEvent)\"\n @keydown.space.prevent=\"handleWellClick(well, $event as unknown as MouseEvent)\"\n >\n <!-- Well label text (from metadata.label) -->\n <span\n v-if=\"getWellLabel(well)\"\n class=\"mld-well-plate__label\"\n >\n {{ getWellLabel(well) }}\n </span>\n <!-- Sample type indicator (S/B/Q/C) -->\n <span\n v-else-if=\"getSampleTypeIndicator(well)\"\n class=\"mld-well-plate__indicator\"\n >\n {{ getSampleTypeIndicator(well) }}\n </span>\n <!-- Well ID -->\n <span\n v-else-if=\"props.showWellIds\"\n class=\"mld-well-plate__well-id\"\n >\n {{ well.id }}\n </span>\n\n <!-- Badge (injection count or custom method) -->\n <span\n v-if=\"getWellBadge(well)\"\n class=\"mld-well-plate__badge\"\n :style=\"{ backgroundColor: getWellBadge(well)!.color }\"\n :title=\"getWellBadge(well)!.text === '+' ? 'Custom method' : `${getWellBadge(well)!.text}x injections`\"\n >\n {{ getWellBadge(well)!.text }}\n </span>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Heatmap legend (inside content wrapper to match table width) -->\n <div\n v-if=\"props.heatmap?.enabled && props.heatmap.showLegend\"\n class=\"mld-well-plate__legend\"\n >\n <span class=\"mld-well-plate__legend-label\">{{ props.heatmap.min ?? 0 }}</span>\n <div class=\"mld-well-plate__legend-bar\">\n <div\n v-for=\"(color, index) in (props.heatmap.colorScale === 'custom' && props.heatmap.customColors?.length ? props.heatmap.customColors : heatmapColors[props.heatmap.colorScale || 'viridis'])\"\n :key=\"index\"\n class=\"mld-well-plate__legend-segment\"\n :style=\"{ backgroundColor: color }\"\n />\n </div>\n <span class=\"mld-well-plate__legend-label\">{{ props.heatmap.max ?? 1 }}</span>\n </div>\n\n <!-- Sample type legend bar -->\n <div v-if=\"props.showLegend\" class=\"mld-well-plate__sample-legend\">\n <div\n v-for=\"item in activeLegendItems\"\n :key=\"item.type\"\n class=\"mld-well-plate__sample-legend-item\"\n >\n <span\n class=\"mld-well-plate__sample-legend-dot\"\n :style=\"{ backgroundColor: `${item.color}26`, border: `1px solid ${item.color}66` }\"\n />\n <span class=\"mld-well-plate__sample-legend-text\">{{ item.label }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Edit popup -->\n <WellEditPopup\n v-if=\"editable && editingWellId\"\n :well-id=\"editingWellId\"\n :well-data=\"wells[editingWellId]\"\n :edit-fields=\"editFields\"\n :default-injection-volume=\"defaultInjectionVolume\"\n :position=\"editPopupPosition\"\n @save=\"handleEditSave\"\n @clear=\"handleEditClear\"\n @close=\"handleEditClose\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/well-plate.css';\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_Fragment","_renderList","_openBlock","_toDisplayString","_normalizeClass","_normalizeStyle","_withModifiers","_createBlock","WellEditPopup"],"mappings":"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"}
1
+ {"version":3,"file":"WellPlate.vue.js","sources":["../../src/components/WellPlate.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted } from 'vue'\nimport type { WellPlateFormat, WellPlateSelectionMode, WellPlateSize, Well, HeatmapConfig, WellShape, WellEditField, WellEditData, WellLegendItem, ColumnCondition, RowCondition } from '../types'\nimport WellEditPopup from './WellEditPopup.vue'\n\ninterface Props {\n modelValue?: string[]\n format?: WellPlateFormat\n wells?: Record<string, Partial<Well>>\n selectionMode?: WellPlateSelectionMode\n showLabels?: boolean\n showWellIds?: boolean\n showSampleTypeIndicator?: boolean\n heatmap?: HeatmapConfig\n sampleColors?: Record<string, string>\n zoom?: number\n disabled?: boolean\n readonly?: boolean\n size?: WellPlateSize\n wellShape?: WellShape\n showWellLabels?: boolean\n showBadges?: boolean\n editable?: boolean\n editFields?: WellEditField[]\n defaultInjectionVolume?: number\n showLegend?: boolean\n legendItems?: WellLegendItem[]\n columnConditions?: ColumnCondition[]\n rowConditions?: RowCondition[]\n}\n\n// Drag state for moving wells\nconst dragSourceWell = ref<string | null>(null)\nconst dragTargetWell = ref<string | null>(null)\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n format: 96,\n wells: () => ({}),\n selectionMode: 'multiple',\n showLabels: true,\n showWellIds: false,\n showSampleTypeIndicator: false,\n heatmap: () => ({ enabled: false }),\n sampleColors: () => ({}),\n zoom: 1,\n disabled: false,\n readonly: false,\n size: 'md',\n wellShape: 'rounded',\n showWellLabels: false,\n showBadges: false,\n editable: false,\n editFields: () => ['label', 'sampleType', 'injectionVolume', 'injectionCount', 'customMethod'],\n defaultInjectionVolume: 5,\n showLegend: false,\n legendItems: undefined,\n columnConditions: () => [],\n rowConditions: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [wellIds: string[]]\n 'well-click': [wellId: string, event: MouseEvent]\n 'well-hover': [wellId: string | null, event?: MouseEvent]\n 'selection-change': [wellIds: string[]]\n 'context-menu': [wellId: string, event: MouseEvent]\n 'well-move': [sourceWellId: string, targetWellId: string]\n 'well-edit': [wellId: string, data: WellEditData]\n 'well-clear': [wellId: string]\n}>()\n\nconst plateRef = ref<HTMLElement | null>(null)\nconst tableRef = ref<HTMLElement | null>(null)\nconst isDragging = ref(false)\nconst dragStart = ref<{ row: number; col: number } | null>(null)\nconst dragEnd = ref<{ row: number; col: number } | null>(null)\nconst hoveredWell = ref<string | null>(null)\n\n// Edit popup state\nconst editingWellId = ref<string | null>(null)\nconst editPopupPosition = ref({ x: 0, y: 0 })\n\nconst PLATE_CONFIGS: Record<WellPlateFormat, { rows: number; cols: number }> = {\n 6: { rows: 2, cols: 3 },\n 12: { rows: 3, cols: 4 },\n 24: { rows: 4, cols: 6 },\n 48: { rows: 6, cols: 8 },\n 54: { rows: 6, cols: 9 },\n 96: { rows: 8, cols: 12 },\n 384: { rows: 16, cols: 24 },\n}\n\nconst plateConfig = computed(() => PLATE_CONFIGS[props.format])\n\nconst rowLabels = computed(() =>\n Array.from({ length: plateConfig.value.rows }, (_, i) => String.fromCharCode(65 + i))\n)\n\nconst colLabels = computed(() =>\n Array.from({ length: plateConfig.value.cols }, (_, i) => i + 1)\n)\n\nconst wellGrid = computed(() => {\n const grid: Well[][] = []\n for (let row = 0; row < plateConfig.value.rows; row++) {\n const rowWells: Well[] = []\n for (let col = 0; col < plateConfig.value.cols; col++) {\n const id = `${rowLabels.value[row]}${col + 1}`\n const wellData = props.wells[id] || {}\n rowWells.push({\n id,\n row,\n col,\n state: wellData.state || 'empty',\n sampleType: wellData.sampleType,\n value: wellData.value,\n metadata: wellData.metadata,\n })\n }\n grid.push(rowWells)\n }\n return grid\n})\n\nconst selectedWellSet = computed(() => new Set(props.modelValue))\n\nconst dragSelectedWells = computed(() => {\n if (!isDragging.value || !dragStart.value || !dragEnd.value) return new Set<string>()\n\n const minRow = Math.min(dragStart.value.row, dragEnd.value.row)\n const maxRow = Math.max(dragStart.value.row, dragEnd.value.row)\n const minCol = Math.min(dragStart.value.col, dragEnd.value.col)\n const maxCol = Math.max(dragStart.value.col, dragEnd.value.col)\n\n const wells = new Set<string>()\n for (let row = minRow; row <= maxRow; row++) {\n for (let col = minCol; col <= maxCol; col++) {\n const id = `${rowLabels.value[row]}${col + 1}`\n wells.add(id)\n }\n }\n return wells\n})\n\n// Size presets with pixel values for reliable sizing\nconst sizeConfig = computed(() => {\n const sizes = {\n sm: { cellWidth: '40px', cellHeight: '40px', headerWidth: '32px', headerHeight: '32px', fontSize: '0.625rem', gap: '2px' },\n md: { cellWidth: '56px', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '3px' },\n lg: { cellWidth: '80px', cellHeight: '40px', headerWidth: '40px', headerHeight: '32px', fontSize: '0.875rem', gap: '4px' },\n xl: { cellWidth: '96px', cellHeight: '48px', headerWidth: '48px', headerHeight: '40px', fontSize: '1rem', gap: '4px' },\n fill: { cellWidth: '100%', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '2px' },\n }\n return sizes[props.size]\n})\n\nconst isFillMode = computed(() => props.size === 'fill')\n\n// Default legend items\nconst defaultLegendItems: WellLegendItem[] = [\n { type: 'sample', label: 'Sample', color: '#10b981' },\n { type: 'blank', label: 'Blank', color: '#f97316' },\n { type: 'qc', label: 'QC', color: '#8b5cf6' },\n]\n\nconst activeLegendItems = computed(() => props.legendItems ?? defaultLegendItems)\n\n// --- Condition header helpers ---\n\nconst hasColumnConditions = computed(() => props.columnConditions.length > 0)\nconst hasRowConditions = computed(() => props.rowConditions.length > 0)\n\n// Map column index (1-based) → ColumnCondition\nconst colConditionMap = computed(() => {\n const map = new Map<number, { condition: ColumnCondition; indexInGroup: number }>()\n for (const cond of props.columnConditions) {\n cond.cols.forEach((col, i) => {\n map.set(col, { condition: cond, indexInGroup: i })\n })\n }\n return map\n})\n\n// Map row letter → RowCondition\nconst rowConditionMap = computed(() => {\n const map = new Map<string, { condition: RowCondition; indexInGroup: number }>()\n for (const cond of props.rowConditions) {\n cond.rows.forEach((row, i) => {\n map.set(row, { condition: cond, indexInGroup: i })\n })\n }\n return map\n})\n\ntype ColSpan = { condition: ColumnCondition; colspan: number } | { gap: true; colspan: number }\ntype RowSpan = { condition: RowCondition; rowspan: number; startRow: number } | { gap: true; rowspan: number; startRow: number }\n\n// Build column condition header spans for the label row (drug name + unit)\nconst colConditionSpans = computed<ColSpan[]>(() => {\n const spans: ColSpan[] = []\n const cols = colLabels.value\n let i = 0\n while (i < cols.length) {\n const entry = colConditionMap.value.get(cols[i])\n if (entry?.indexInGroup === 0) {\n spans.push({ condition: entry.condition, colspan: entry.condition.cols.length })\n i += entry.condition.cols.length\n } else {\n let gapCount = 0\n while (i + gapCount < cols.length && !colConditionMap.value.has(cols[i + gapCount])) {\n gapCount++\n }\n spans.push({ gap: true, colspan: gapCount || 1 })\n i += gapCount || 1\n }\n }\n return spans\n})\n\n// Build row condition spans for the label column (drug name rotated)\nconst rowConditionSpans = computed<RowSpan[]>(() => {\n const spans: RowSpan[] = []\n const rows = rowLabels.value\n let i = 0\n while (i < rows.length) {\n const entry = rowConditionMap.value.get(rows[i])\n if (entry?.indexInGroup === 0) {\n spans.push({ condition: entry.condition, rowspan: entry.condition.rows.length, startRow: i })\n i += entry.condition.rows.length\n } else {\n let gapCount = 0\n while (i + gapCount < rows.length && !rowConditionMap.value.has(rows[i + gapCount])) {\n gapCount++\n }\n spans.push({ gap: true, rowspan: gapCount || 1, startRow: i })\n i += gapCount || 1\n }\n }\n return spans\n})\n\n// Map row index → span info (only contains entries for first row of each span)\nconst rowConditionSpanByRow = computed(() => {\n const map = new Map<number, RowSpan>()\n for (const span of rowConditionSpans.value) {\n map.set(span.startRow, span)\n }\n return map\n})\n\n// Compute gradient background for a condition cell: opacity ramps from 12% to 50% across the group\nfunction conditionGradientBg(color: string, index: number, total: number): string {\n const t = total <= 1 ? 1 : index / (total - 1)\n const opacity = 0.12 + t * 0.38\n const r = parseInt(color.slice(1, 3), 16)\n const g = parseInt(color.slice(3, 5), 16)\n const b = parseInt(color.slice(5, 7), 16)\n return `rgba(${r}, ${g}, ${b}, ${opacity})`\n}\n\n// Format concentration value (drop trailing zeros)\nfunction formatConc(value: number): string {\n if (value >= 1000) return `${value / 1000}k`\n if (value < 0.01) return value.toExponential(0)\n return String(value)\n}\n\n// Sample type colors (matching MSExpDesigner)\nconst defaultSampleTypeColors: Record<string, { bg: string; border: string }> = {\n sample: { bg: 'rgba(16, 185, 129, 0.15)', border: 'rgba(16, 185, 129, 0.4)' },\n control: { bg: 'rgba(59, 130, 246, 0.15)', border: 'rgba(59, 130, 246, 0.4)' },\n blank: { bg: 'rgba(249, 115, 22, 0.15)', border: 'rgba(249, 115, 22, 0.4)' },\n qc: { bg: 'rgba(139, 92, 246, 0.15)', border: 'rgba(139, 92, 246, 0.4)' },\n}\n\nconst heatmapColors: Record<string, string[]> = {\n viridis: ['#440154', '#482878', '#3e4989', '#31688e', '#26828e', '#1f9e89', '#35b779', '#6ece58', '#b5de2b', '#fde725'],\n plasma: ['#0d0887', '#46039f', '#7201a8', '#9c179e', '#bd3786', '#d8576b', '#ed7953', '#fb9f3a', '#fdca26', '#f0f921'],\n turbo: ['#30123b', '#4145ab', '#4675ed', '#39a2fc', '#1bcfd4', '#24e79e', '#71f05f', '#c1f034', '#f1c83c', '#f99538', '#e45a31', '#ba2512', '#7a0403'],\n}\n\nfunction getHeatmapColor(value: number | undefined): string | null {\n if (!props.heatmap?.enabled || value === undefined) return null\n\n const min = props.heatmap.min ?? 0\n const max = props.heatmap.max ?? 1\n const normalized = Math.max(0, Math.min(1, (value - min) / (max - min)))\n\n const colors = props.heatmap.colorScale === 'custom' && props.heatmap.customColors?.length\n ? props.heatmap.customColors\n : heatmapColors[props.heatmap.colorScale || 'viridis']\n\n const index = Math.min(Math.floor(normalized * (colors.length - 1)), colors.length - 1)\n return colors[index]\n}\n\nfunction getWellClasses(well: Well): string[] {\n const isWellSelected = isSelected(well.id)\n const isDragOver = dragSelectedWells.value.has(well.id)\n const isHovered = hoveredWell.value === well.id\n const isDisabled = props.disabled || well.state === 'disabled'\n const isDragSource = dragSourceWell.value === well.id\n const isDragTarget = dragTargetWell.value === well.id\n\n const classes = [\n 'mld-well-plate__well',\n props.wellShape === 'circle' ? 'mld-well-plate__well--circle' : 'mld-well-plate__well--rounded',\n ]\n\n if (props.selectionMode === 'drag' && well.sampleType) {\n classes.push('mld-well-plate__well--draggable')\n }\n\n if (isDragSource) classes.push('mld-well-plate__well--drag-source')\n else if (isDragTarget) classes.push('mld-well-plate__well--drag-target')\n else if (isWellSelected) classes.push('mld-well-plate__well--selected')\n else if (isDragOver) classes.push('mld-well-plate__well--drag-over')\n else if (isHovered && !props.readonly) classes.push('mld-well-plate__well--hovered')\n\n if (isDisabled) classes.push('mld-well-plate__well--disabled')\n if (well.state === 'filled') classes.push('mld-well-plate__well--filled')\n\n return classes\n}\n\nfunction getWellStyle(well: Well): Record<string, string> {\n const heatmapColor = getHeatmapColor(well.value)\n if (heatmapColor) {\n return { backgroundColor: heatmapColor, border: '1px solid transparent' }\n }\n\n if (well.sampleType && props.sampleColors[well.sampleType]) {\n const color = props.sampleColors[well.sampleType]\n return {\n backgroundColor: `${color}26`,\n border: `1px solid ${color}66`,\n }\n }\n\n if (well.sampleType && defaultSampleTypeColors[well.sampleType]) {\n const colors = defaultSampleTypeColors[well.sampleType]\n return {\n backgroundColor: colors.bg,\n border: `1px solid ${colors.border}`,\n }\n }\n\n const borderStyle = well.state === 'filled' ? 'solid' : 'dashed'\n return {\n backgroundColor: 'var(--bg-tertiary)',\n border: `1px ${borderStyle} var(--border-color)`,\n }\n}\n\nfunction getSampleTypeIndicator(well: Well): string | null {\n if (!props.showSampleTypeIndicator || !well.sampleType) return null\n const typeMap: Record<string, string> = {\n sample: 'S',\n control: 'C',\n blank: 'B',\n qc: 'Q',\n }\n return typeMap[well.sampleType] || well.sampleType.charAt(0).toUpperCase()\n}\n\nfunction getWellLabel(well: Well): string | undefined {\n if (!props.showWellLabels) return undefined\n return well.metadata?.label as string | undefined\n}\n\nfunction getWellBadge(well: Well): { text: string; color: string } | null {\n if (!props.showBadges || !well.metadata) return null\n const count = well.metadata.injectionCount as number | undefined\n if (count && count > 1) {\n return { text: String(count), color: '#6366f1' }\n }\n if (well.metadata.customMethod) {\n return { text: '+', color: '#ec4899' }\n }\n return null\n}\n\nfunction isSelected(wellId: string): boolean {\n return selectedWellSet.value.has(wellId) || dragSelectedWells.value.has(wellId)\n}\n\nfunction handleWellClick(well: Well, event: MouseEvent) {\n if (props.disabled || props.readonly) return\n\n emit('well-click', well.id, event)\n\n // When editable, open popup instead of modifying selection\n if (props.editable) {\n openEditPopup(well.id, event)\n return\n }\n\n if (props.selectionMode === 'none') return\n\n const isCurrentlySelected = selectedWellSet.value.has(well.id)\n const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey\n\n let newSelection: string[]\n if (props.selectionMode === 'single') {\n newSelection = isCurrentlySelected ? [] : [well.id]\n } else if (isMultiSelect) {\n newSelection = isCurrentlySelected\n ? props.modelValue.filter(id => id !== well.id)\n : [...props.modelValue, well.id]\n } else {\n newSelection = isCurrentlySelected && props.modelValue.length === 1 ? [] : [well.id]\n }\n\n emit('update:modelValue', newSelection)\n emit('selection-change', newSelection)\n}\n\nfunction openEditPopup(wellId: string, event: MouseEvent) {\n const target = event.currentTarget as HTMLElement\n const rect = target.getBoundingClientRect()\n editPopupPosition.value = {\n x: rect.right + 8,\n y: rect.top,\n }\n editingWellId.value = wellId\n}\n\nfunction handleEditSave(data: WellEditData) {\n emit('well-edit', data.wellId, data)\n editingWellId.value = null\n}\n\nfunction handleEditClear() {\n if (editingWellId.value) {\n emit('well-clear', editingWellId.value)\n }\n editingWellId.value = null\n}\n\nfunction handleEditClose() {\n editingWellId.value = null\n}\n\nfunction handleWellMouseDown(well: Well, event: MouseEvent) {\n if (props.disabled || props.readonly || props.selectionMode !== 'rectangle') return\n if (props.editable) return\n if (event.button !== 0) return\n\n isDragging.value = true\n dragStart.value = { row: well.row, col: well.col }\n dragEnd.value = { row: well.row, col: well.col }\n}\n\nfunction handleWellMouseEnter(well: Well, event: MouseEvent) {\n hoveredWell.value = well.id\n emit('well-hover', well.id, event)\n\n if (isDragging.value && props.selectionMode === 'rectangle') {\n dragEnd.value = { row: well.row, col: well.col }\n }\n}\n\nfunction handleWellMouseLeave() {\n hoveredWell.value = null\n emit('well-hover', null)\n}\n\nfunction handleMouseUp() {\n if (!isDragging.value || props.selectionMode !== 'rectangle') return\n\n const newSelection = Array.from(dragSelectedWells.value)\n isDragging.value = false\n dragStart.value = null\n dragEnd.value = null\n\n if (newSelection.length > 0) {\n emit('update:modelValue', newSelection)\n emit('selection-change', newSelection)\n }\n}\n\nfunction handleContextMenu(well: Well, event: MouseEvent) {\n event.preventDefault()\n emit('context-menu', well.id, event)\n}\n\n// Drag handlers for moving well contents\nfunction handleDragStart(well: Well, event: DragEvent) {\n if (props.disabled || props.readonly || props.selectionMode !== 'drag') return\n if (!well.sampleType) return\n\n dragSourceWell.value = well.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', well.id)\n }\n}\n\nfunction handleDragOver(well: Well, event: DragEvent) {\n if (props.selectionMode !== 'drag' || !dragSourceWell.value) return\n event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move'\n }\n dragTargetWell.value = well.id\n}\n\nfunction handleDragLeave() {\n dragTargetWell.value = null\n}\n\nfunction handleDrop(well: Well, event: DragEvent) {\n event.preventDefault()\n if (props.selectionMode !== 'drag' || !dragSourceWell.value) return\n\n const sourceId = dragSourceWell.value\n const targetId = well.id\n\n if (sourceId !== targetId) {\n emit('well-move', sourceId, targetId)\n }\n\n dragSourceWell.value = null\n dragTargetWell.value = null\n}\n\nfunction handleDragEnd() {\n dragSourceWell.value = null\n dragTargetWell.value = null\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n if (props.disabled || props.readonly) return\n\n if (event.key === 'Escape') {\n if (editingWellId.value) {\n editingWellId.value = null\n return\n }\n emit('update:modelValue', [])\n emit('selection-change', [])\n }\n\n if ((event.key === 'a' || event.key === 'A') && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n const allWells = wellGrid.value.flat().map(w => w.id)\n emit('update:modelValue', allWells)\n emit('selection-change', allWells)\n }\n}\n\nonMounted(() => {\n document.addEventListener('mouseup', handleMouseUp)\n document.addEventListener('keydown', handleKeyDown)\n})\n\nonUnmounted(() => {\n document.removeEventListener('mouseup', handleMouseUp)\n document.removeEventListener('keydown', handleKeyDown)\n})\n\nconst containerStyle = computed(() =>\n isFillMode.value ? {} : {\n transform: `scale(${props.zoom})`,\n transformOrigin: 'top left',\n }\n)\n\nconst tableStyle = computed(() => ({\n borderCollapse: 'separate' as const,\n borderSpacing: sizeConfig.value.gap,\n ...(isFillMode.value ? { width: '100%', tableLayout: 'fixed' as const } : {}),\n}))\n</script>\n\n<template>\n <div\n ref=\"plateRef\"\n :class=\"['mld-well-plate', isFillMode ? 'mld-well-plate--fill' : 'mld-well-plate--inline']\"\n :style=\"containerStyle\"\n >\n <div class=\"mld-well-plate__scroll\">\n <div class=\"mld-well-plate__content\">\n <table\n ref=\"tableRef\"\n class=\"mld-well-plate__table\"\n role=\"grid\"\n :aria-label=\"`${props.format}-well plate`\"\n :style=\"tableStyle\"\n >\n <!-- Column condition label row (drug name + unit) -->\n <thead v-if=\"hasColumnConditions\">\n <tr>\n <!-- Spacer for row header column -->\n <th :style=\"{ width: sizeConfig.headerWidth }\"></th>\n <!-- Spacer for row condition column -->\n <th v-if=\"hasRowConditions\" :style=\"{ width: sizeConfig.headerWidth }\"></th>\n <template v-for=\"(span, idx) in colConditionSpans\" :key=\"'clabel-' + idx\">\n <th\n v-if=\"'condition' in span\"\n :colspan=\"span.colspan\"\n class=\"mld-well-plate__condition-label\"\n :style=\"{\n height: sizeConfig.headerHeight,\n fontSize: sizeConfig.fontSize,\n color: span.condition.color,\n }\"\n >\n {{ span.condition.label }}<template v-if=\"span.condition.unit\"> ({{ span.condition.unit }})</template>\n </th>\n <th v-else :colspan=\"span.colspan\"></th>\n </template>\n </tr>\n </thead>\n <!-- Column headers (with concentration overlay when conditions present) -->\n <thead v-if=\"props.showLabels\">\n <tr>\n <th :style=\"{ width: sizeConfig.headerWidth, height: sizeConfig.headerHeight }\"></th>\n <!-- Spacer for row condition column in column header row -->\n <th v-if=\"hasRowConditions\" :style=\"{ width: sizeConfig.headerWidth, height: sizeConfig.headerHeight }\"></th>\n <th\n v-for=\"col in colLabels\"\n :key=\"col\"\n class=\"mld-well-plate__col-header\"\n :style=\"{ height: sizeConfig.headerHeight, fontSize: sizeConfig.fontSize }\"\n >\n <template v-for=\"entry in [colConditionMap.get(col)]\" :key=\"col\">\n <span\n v-if=\"entry\"\n class=\"mld-well-plate__condition-cell\"\n :style=\"{\n backgroundColor: conditionGradientBg(\n entry.condition.color,\n entry.indexInGroup,\n entry.condition.cols.length,\n ),\n }\"\n >\n {{ formatConc(entry.condition.concentrations[entry.indexInGroup]) }}\n </span>\n <template v-else>{{ col }}</template>\n </template>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(row, rowIndex) in wellGrid\" :key=\"rowIndex\" role=\"row\">\n <!-- Row condition label cell (drug name, spans multiple rows) -->\n <template v-if=\"hasRowConditions && rowConditionSpanByRow.has(rowIndex)\">\n <td\n v-for=\"span in [rowConditionSpanByRow.get(rowIndex)!]\"\n :key=\"rowIndex\"\n :rowspan=\"span.rowspan\"\n :class=\"[\n 'mld-well-plate__row-condition-label',\n 'gap' in span ? 'mld-well-plate__row-condition-label--empty' : '',\n ]\"\n :style=\"{\n width: sizeConfig.headerWidth,\n minWidth: sizeConfig.headerWidth,\n fontSize: sizeConfig.fontSize,\n ...('condition' in span ? { color: span.condition.color } : {}),\n }\"\n >\n <div v-if=\"'condition' in span\" class=\"mld-well-plate__row-condition-wrap\">\n <span class=\"mld-well-plate__row-condition-text\">{{ span.condition.label }}</span>\n </div>\n </td>\n </template>\n <!-- Row header (with concentration overlay when conditions present) -->\n <td\n v-if=\"props.showLabels\"\n class=\"mld-well-plate__row-header\"\n :style=\"{\n width: sizeConfig.headerWidth,\n height: sizeConfig.cellHeight,\n minWidth: sizeConfig.headerWidth,\n minHeight: sizeConfig.cellHeight,\n fontSize: sizeConfig.fontSize,\n }\"\n >\n <template v-for=\"entry in [rowConditionMap.get(rowLabels[rowIndex])]\" :key=\"rowIndex\">\n <span\n v-if=\"entry\"\n class=\"mld-well-plate__condition-cell\"\n :style=\"{\n backgroundColor: conditionGradientBg(\n entry.condition.color,\n entry.indexInGroup,\n entry.condition.rows.length,\n ),\n }\"\n >\n {{ formatConc(entry.condition.concentrations[entry.indexInGroup]) }}\n </span>\n <template v-else>{{ rowLabels[rowIndex] }}</template>\n </template>\n </td>\n\n <!-- Wells -->\n <td v-for=\"well in row\" :key=\"well.id\" class=\"mld-well-plate__cell\">\n <div\n role=\"gridcell\"\n tabindex=\"0\"\n :aria-label=\"`Well ${well.id}${well.sampleType ? `, Sample type: ${well.sampleType}` : ''}${well.value !== undefined ? `, Value: ${well.value}` : ''}`\"\n :aria-selected=\"isSelected(well.id)\"\n :aria-disabled=\"props.disabled || well.state === 'disabled'\"\n :draggable=\"props.selectionMode === 'drag' && !!well.sampleType\"\n :class=\"getWellClasses(well)\"\n :style=\"{\n width: isFillMode ? '100%' : sizeConfig.cellWidth,\n height: sizeConfig.cellHeight,\n minWidth: isFillMode ? '0' : sizeConfig.cellWidth,\n minHeight: sizeConfig.cellHeight,\n boxSizing: 'border-box',\n fontSize: sizeConfig.fontSize,\n ...getWellStyle(well),\n }\"\n :title=\"`${well.id}${well.sampleType ? `: ${well.sampleType}` : ''}${getWellLabel(well) ? ` - ${getWellLabel(well)}` : ''}`\"\n @click=\"handleWellClick(well, $event)\"\n @mousedown=\"handleWellMouseDown(well, $event)\"\n @mouseenter=\"handleWellMouseEnter(well, $event)\"\n @mouseleave=\"handleWellMouseLeave\"\n @contextmenu=\"handleContextMenu(well, $event)\"\n @dragstart=\"handleDragStart(well, $event)\"\n @dragover.prevent=\"handleDragOver(well, $event)\"\n @dragleave=\"handleDragLeave\"\n @drop.prevent=\"handleDrop(well, $event)\"\n @dragend=\"handleDragEnd\"\n @keydown.enter=\"handleWellClick(well, $event as unknown as MouseEvent)\"\n @keydown.space.prevent=\"handleWellClick(well, $event as unknown as MouseEvent)\"\n >\n <!-- Well label text (from metadata.label) -->\n <span\n v-if=\"getWellLabel(well)\"\n class=\"mld-well-plate__label\"\n >\n {{ getWellLabel(well) }}\n </span>\n <!-- Sample type indicator (S/B/Q/C) -->\n <span\n v-else-if=\"getSampleTypeIndicator(well)\"\n class=\"mld-well-plate__indicator\"\n >\n {{ getSampleTypeIndicator(well) }}\n </span>\n <!-- Well ID -->\n <span\n v-else-if=\"props.showWellIds\"\n class=\"mld-well-plate__well-id\"\n >\n {{ well.id }}\n </span>\n\n <!-- Badge (injection count or custom method) -->\n <span\n v-if=\"getWellBadge(well)\"\n class=\"mld-well-plate__badge\"\n :style=\"{ backgroundColor: getWellBadge(well)!.color }\"\n :title=\"getWellBadge(well)!.text === '+' ? 'Custom method' : `${getWellBadge(well)!.text}x injections`\"\n >\n {{ getWellBadge(well)!.text }}\n </span>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Heatmap legend (inside content wrapper to match table width) -->\n <div\n v-if=\"props.heatmap?.enabled && props.heatmap.showLegend\"\n class=\"mld-well-plate__legend\"\n >\n <span class=\"mld-well-plate__legend-label\">{{ props.heatmap.min ?? 0 }}</span>\n <div class=\"mld-well-plate__legend-bar\">\n <div\n v-for=\"(color, index) in (props.heatmap.colorScale === 'custom' && props.heatmap.customColors?.length ? props.heatmap.customColors : heatmapColors[props.heatmap.colorScale || 'viridis'])\"\n :key=\"index\"\n class=\"mld-well-plate__legend-segment\"\n :style=\"{ backgroundColor: color }\"\n />\n </div>\n <span class=\"mld-well-plate__legend-label\">{{ props.heatmap.max ?? 1 }}</span>\n </div>\n\n <!-- Sample type legend bar -->\n <div v-if=\"props.showLegend\" class=\"mld-well-plate__sample-legend\">\n <div\n v-for=\"item in activeLegendItems\"\n :key=\"item.type\"\n class=\"mld-well-plate__sample-legend-item\"\n >\n <span\n class=\"mld-well-plate__sample-legend-dot\"\n :style=\"{ backgroundColor: `${item.color}26`, border: `1px solid ${item.color}66` }\"\n />\n <span class=\"mld-well-plate__sample-legend-text\">{{ item.label }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Edit popup -->\n <WellEditPopup\n v-if=\"editable && editingWellId\"\n :well-id=\"editingWellId\"\n :well-data=\"wells[editingWellId]\"\n :edit-fields=\"editFields\"\n :default-injection-volume=\"defaultInjectionVolume\"\n :position=\"editPopupPosition\"\n @save=\"handleEditSave\"\n @clear=\"handleEditClear\"\n @close=\"handleEditClose\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/well-plate.css';\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_normalizeStyle","_openBlock","_Fragment","_renderList","_createTextVNode","_normalizeClass","_toDisplayString","_withModifiers","_createBlock","WellEditPopup"],"mappings":"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"}
package/dist/styles.css CHANGED
@@ -1553,6 +1553,8 @@ html.dark .focus\:ring-offset-2:focus {
1553
1553
  border-radius: 0.5rem;
1554
1554
  font-weight: 500;
1555
1555
  transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
1556
+ line-height: 1.25;
1557
+ white-space: nowrap;
1556
1558
  cursor: pointer;
1557
1559
  border: none;
1558
1560
  }
@@ -4817,22 +4819,6 @@ html.dark .mld-toggle__track:focus-visible {
4817
4819
  border: none !important;
4818
4820
  background: transparent !important;
4819
4821
  }
4820
- .mld-well-plate__table--fill {
4821
- width: 100%;
4822
- table-layout: fixed;
4823
- }
4824
- .mld-well-plate__table--sm {
4825
- border-spacing: 2px;
4826
- }
4827
- .mld-well-plate__table--md {
4828
- border-spacing: 4px;
4829
- }
4830
- .mld-well-plate__table--lg {
4831
- border-spacing: 4px;
4832
- }
4833
- .mld-well-plate__table--xl {
4834
- border-spacing: 6px;
4835
- }
4836
4822
  /* Header cells */
4837
4823
  .mld-well-plate__col-header,
4838
4824
  .mld-well-plate__row-header {
@@ -4840,22 +4826,6 @@ html.dark .mld-toggle__track:focus-visible {
4840
4826
  text-align: center;
4841
4827
  color: var(--text-muted);
4842
4828
  }
4843
- .mld-well-plate__col-header--sm,
4844
- .mld-well-plate__row-header--sm {
4845
- font-size: 0.625rem;
4846
- }
4847
- .mld-well-plate__col-header--md,
4848
- .mld-well-plate__row-header--md {
4849
- font-size: 0.75rem;
4850
- }
4851
- .mld-well-plate__col-header--lg,
4852
- .mld-well-plate__row-header--lg {
4853
- font-size: 0.875rem;
4854
- }
4855
- .mld-well-plate__col-header--xl,
4856
- .mld-well-plate__row-header--xl {
4857
- font-size: 1rem;
4858
- }
4859
4829
  .mld-well-plate__row-header {
4860
4830
  vertical-align: middle;
4861
4831
  }
@@ -4874,18 +4844,6 @@ html.dark .mld-toggle__track:focus-visible {
4874
4844
  background-color: var(--bg-tertiary);
4875
4845
  border: 1px dashed var(--border-color);
4876
4846
  }
4877
- .mld-well-plate__well--sm {
4878
- font-size: 0.625rem;
4879
- }
4880
- .mld-well-plate__well--md {
4881
- font-size: 0.75rem;
4882
- }
4883
- .mld-well-plate__well--lg {
4884
- font-size: 0.875rem;
4885
- }
4886
- .mld-well-plate__well--xl {
4887
- font-size: 1rem;
4888
- }
4889
4847
  .mld-well-plate__well--rounded {
4890
4848
  border-radius: 0.5rem;
4891
4849
  }
@@ -4964,6 +4922,51 @@ html.dark .mld-toggle__track:focus-visible {
4964
4922
  background-color: rgba(139, 92, 246, 0.15);
4965
4923
  border: 1px solid rgba(139, 92, 246, 0.4);
4966
4924
  }
4925
+ /* Condition header - drug label row */
4926
+ .mld-well-plate__condition-label {
4927
+ font-weight: 600;
4928
+ text-align: center;
4929
+ white-space: nowrap;
4930
+ overflow: hidden;
4931
+ text-overflow: ellipsis;
4932
+ letter-spacing: 0.01em;
4933
+ padding: 2px 4px !important;
4934
+ }
4935
+ /* Condition cell - inner span with gradient bg (avoids table !important reset) */
4936
+ .mld-well-plate__condition-cell {
4937
+ display: flex;
4938
+ align-items: center;
4939
+ justify-content: center;
4940
+ width: 100%;
4941
+ height: 100%;
4942
+ border-radius: 4px;
4943
+ font-weight: 600;
4944
+ transition: background-color 0.15s ease;
4945
+ }
4946
+ /* Row condition label cell (rotated text) */
4947
+ .mld-well-plate__row-condition-label {
4948
+ text-align: center;
4949
+ vertical-align: middle;
4950
+ padding: 0 !important;
4951
+ position: relative;
4952
+ height: 1px; /* Trick: allows absolute child to use 100% height in rowspan cells */
4953
+ }
4954
+ .mld-well-plate__row-condition-wrap {
4955
+ position: absolute;
4956
+ inset: 0;
4957
+ display: flex;
4958
+ align-items: center;
4959
+ justify-content: center;
4960
+ }
4961
+ .mld-well-plate__row-condition-text {
4962
+ writing-mode: vertical-lr;
4963
+ transform: rotate(180deg);
4964
+ font-weight: 600;
4965
+ white-space: nowrap;
4966
+ overflow: hidden;
4967
+ text-overflow: ellipsis;
4968
+ letter-spacing: 0.01em;
4969
+ }
4967
4970
  /* Heatmap legend */
4968
4971
  .mld-well-plate__legend {
4969
4972
  margin-top: 0.5rem;
@@ -9060,6 +9063,11 @@ html.dark .mld-dataframe__loading {
9060
9063
  letter-spacing: 0.025em;
9061
9064
  color: var(--text-muted);
9062
9065
  }
9066
+ .mld-rack-editor__toolbar-section {
9067
+ display: flex;
9068
+ align-items: center;
9069
+ gap: 0.5rem;
9070
+ }
9063
9071
  .mld-rack-editor__toolbar-divider {
9064
9072
  width: 1px;
9065
9073
  height: 1.5rem;
@@ -11121,6 +11129,9 @@ html.dark .mld-settings-modal__option-btn--active {
11121
11129
  .mld-datetime-picker__footer-btn:hover {
11122
11130
  opacity: 0.8;
11123
11131
  }
11132
+ .mld-datetime-picker__footer-btn + .mld-datetime-picker__footer-btn {
11133
+ margin-left: 0.75rem;
11134
+ }
11124
11135
  .mld-datetime-picker__footer-btn--muted {
11125
11136
  color: var(--text-muted);
11126
11137
  }
@@ -11909,6 +11920,8 @@ html.dark .mld-settings-modal__option-btn--active {
11909
11920
  border-radius: 0.5rem;
11910
11921
  font-weight: 500;
11911
11922
  transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
11923
+ line-height: 1.25;
11924
+ white-space: nowrap;
11912
11925
  cursor: pointer;
11913
11926
  border: none;
11914
11927
  }
@@ -17196,22 +17209,6 @@ to {
17196
17209
  border: none !important;
17197
17210
  background: transparent !important;
17198
17211
  }
17199
- .mld-well-plate__table--fill {
17200
- width: 100%;
17201
- table-layout: fixed;
17202
- }
17203
- .mld-well-plate__table--sm {
17204
- border-spacing: 2px;
17205
- }
17206
- .mld-well-plate__table--md {
17207
- border-spacing: 4px;
17208
- }
17209
- .mld-well-plate__table--lg {
17210
- border-spacing: 4px;
17211
- }
17212
- .mld-well-plate__table--xl {
17213
- border-spacing: 6px;
17214
- }
17215
17212
 
17216
17213
  /* Header cells */
17217
17214
  .mld-well-plate__col-header,
@@ -17220,22 +17217,6 @@ to {
17220
17217
  text-align: center;
17221
17218
  color: var(--text-muted);
17222
17219
  }
17223
- .mld-well-plate__col-header--sm,
17224
- .mld-well-plate__row-header--sm {
17225
- font-size: 0.625rem;
17226
- }
17227
- .mld-well-plate__col-header--md,
17228
- .mld-well-plate__row-header--md {
17229
- font-size: 0.75rem;
17230
- }
17231
- .mld-well-plate__col-header--lg,
17232
- .mld-well-plate__row-header--lg {
17233
- font-size: 0.875rem;
17234
- }
17235
- .mld-well-plate__col-header--xl,
17236
- .mld-well-plate__row-header--xl {
17237
- font-size: 1rem;
17238
- }
17239
17220
  .mld-well-plate__row-header {
17240
17221
  vertical-align: middle;
17241
17222
  }
@@ -17256,18 +17237,6 @@ to {
17256
17237
  background-color: var(--bg-tertiary);
17257
17238
  border: 1px dashed var(--border-color);
17258
17239
  }
17259
- .mld-well-plate__well--sm {
17260
- font-size: 0.625rem;
17261
- }
17262
- .mld-well-plate__well--md {
17263
- font-size: 0.75rem;
17264
- }
17265
- .mld-well-plate__well--lg {
17266
- font-size: 0.875rem;
17267
- }
17268
- .mld-well-plate__well--xl {
17269
- font-size: 1rem;
17270
- }
17271
17240
  .mld-well-plate__well--rounded {
17272
17241
  border-radius: 0.5rem;
17273
17242
  }
@@ -17353,6 +17322,54 @@ to {
17353
17322
  border: 1px solid rgba(139, 92, 246, 0.4);
17354
17323
  }
17355
17324
 
17325
+ /* Condition header - drug label row */
17326
+ .mld-well-plate__condition-label {
17327
+ font-weight: 600;
17328
+ text-align: center;
17329
+ white-space: nowrap;
17330
+ overflow: hidden;
17331
+ text-overflow: ellipsis;
17332
+ letter-spacing: 0.01em;
17333
+ padding: 2px 4px !important;
17334
+ }
17335
+
17336
+ /* Condition cell - inner span with gradient bg (avoids table !important reset) */
17337
+ .mld-well-plate__condition-cell {
17338
+ display: flex;
17339
+ align-items: center;
17340
+ justify-content: center;
17341
+ width: 100%;
17342
+ height: 100%;
17343
+ border-radius: 4px;
17344
+ font-weight: 600;
17345
+ transition: background-color 0.15s ease;
17346
+ }
17347
+
17348
+ /* Row condition label cell (rotated text) */
17349
+ .mld-well-plate__row-condition-label {
17350
+ text-align: center;
17351
+ vertical-align: middle;
17352
+ padding: 0 !important;
17353
+ position: relative;
17354
+ height: 1px; /* Trick: allows absolute child to use 100% height in rowspan cells */
17355
+ }
17356
+ .mld-well-plate__row-condition-wrap {
17357
+ position: absolute;
17358
+ inset: 0;
17359
+ display: flex;
17360
+ align-items: center;
17361
+ justify-content: center;
17362
+ }
17363
+ .mld-well-plate__row-condition-text {
17364
+ writing-mode: vertical-lr;
17365
+ transform: rotate(180deg);
17366
+ font-weight: 600;
17367
+ white-space: nowrap;
17368
+ overflow: hidden;
17369
+ text-overflow: ellipsis;
17370
+ letter-spacing: 0.01em;
17371
+ }
17372
+
17356
17373
  /* Heatmap legend */
17357
17374
  .mld-well-plate__legend {
17358
17375
  margin-top: 0.5rem;
@@ -17575,6 +17592,11 @@ to {
17575
17592
  letter-spacing: 0.025em;
17576
17593
  color: var(--text-muted);
17577
17594
  }
17595
+ .mld-rack-editor__toolbar-section {
17596
+ display: flex;
17597
+ align-items: center;
17598
+ gap: 0.5rem;
17599
+ }
17578
17600
  .mld-rack-editor__toolbar-divider {
17579
17601
  width: 1px;
17580
17602
  height: 1.5rem;
@@ -22457,6 +22479,9 @@ to { transform: rotate(360deg);
22457
22479
  .mld-datetime-picker__footer-btn:hover {
22458
22480
  opacity: 0.8;
22459
22481
  }
22482
+ .mld-datetime-picker__footer-btn + .mld-datetime-picker__footer-btn {
22483
+ margin-left: 0.75rem;
22484
+ }
22460
22485
  .mld-datetime-picker__footer-btn--muted {
22461
22486
  color: var(--text-muted);
22462
22487
  }
@@ -127,6 +127,18 @@ export interface WellLegendItem {
127
127
  label: string;
128
128
  color: string;
129
129
  }
130
+ export interface PlateCondition {
131
+ label: string;
132
+ color: string;
133
+ concentrations: number[];
134
+ unit?: string;
135
+ }
136
+ export interface ColumnCondition extends PlateCondition {
137
+ cols: number[];
138
+ }
139
+ export interface RowCondition extends PlateCondition {
140
+ rows: string[];
141
+ }
130
142
  export interface Rack {
131
143
  id: string;
132
144
  name: string;
@@ -1,3 +1,3 @@
1
- export type { ContainerDirection, ButtonVariant, ButtonSize, InputType, ModalSize, AlertType, Toast, TabItem, SelectOption, RadioOption, FormFieldProps, SidebarToolSection, CollapsibleState, TopBarVariant, TopBarPage, TopBarTab, TopBarTabOption, TopBarSettingsConfig, WellPlateFormat, WellState, WellPlateSelectionMode, WellPlateSize, WellShape, Well, HeatmapColorScale, HeatmapConfig, SlotPosition, WellExtendedData, WellEditData, WellEditField, WellLegendItem, Rack, SampleType, PlateMap, PlateMapEditorState, ProtocolStepType, ProtocolStepStatus, ProtocolStep, SampleGroup, GroupItem, FileUploaderMode, SegmentedOption, SegmentedControlVariant, SegmentedControlSize, MultiSelectOption, MultiSelectSize, PillVariant, PillSize, CalendarSelectionMode, CalendarMarker, CalendarDayContext, SortDirection, SortState, DataFrameColumn, PaginationState, SpinnerSize, SpinnerVariant, DividerSpacing, StatusType, ProgressVariant, ProgressSize, AvatarSize, EmptyStateColor, EmptyStateSize, BreadcrumbItem, TooltipPosition, ConfirmVariant, SettingsTab, NumberNotation, UnitOption, WizardStep, WizardStepState, AuditEntryType, AuditEntry, BatchItemStatus, BatchItem, BatchSummary, TimePickerFormat, TimeRange, ScheduleView, ScheduleEventStatus, ScheduleEvent, ScheduleBlockedSlot, ScheduleSlotContext, ScheduleEventCreateContext, ScheduleEventUpdateContext, ResourceStatus, ResourceSpec, } from './components';
1
+ export type { ContainerDirection, ButtonVariant, ButtonSize, InputType, ModalSize, AlertType, Toast, TabItem, SelectOption, RadioOption, FormFieldProps, SidebarToolSection, CollapsibleState, TopBarVariant, TopBarPage, TopBarTab, TopBarTabOption, TopBarSettingsConfig, WellPlateFormat, WellState, WellPlateSelectionMode, WellPlateSize, WellShape, Well, HeatmapColorScale, HeatmapConfig, SlotPosition, WellExtendedData, WellEditData, WellEditField, WellLegendItem, PlateCondition, ColumnCondition, RowCondition, Rack, SampleType, PlateMap, PlateMapEditorState, ProtocolStepType, ProtocolStepStatus, ProtocolStep, SampleGroup, GroupItem, FileUploaderMode, SegmentedOption, SegmentedControlVariant, SegmentedControlSize, MultiSelectOption, MultiSelectSize, PillVariant, PillSize, CalendarSelectionMode, CalendarMarker, CalendarDayContext, SortDirection, SortState, DataFrameColumn, PaginationState, SpinnerSize, SpinnerVariant, DividerSpacing, StatusType, ProgressVariant, ProgressSize, AvatarSize, EmptyStateColor, EmptyStateSize, BreadcrumbItem, TooltipPosition, ConfirmVariant, SettingsTab, NumberNotation, UnitOption, WizardStep, WizardStepState, AuditEntryType, AuditEntry, BatchItemStatus, BatchItem, BatchSummary, TimePickerFormat, TimeRange, ScheduleView, ScheduleEventStatus, ScheduleEvent, ScheduleBlockedSlot, ScheduleSlotContext, ScheduleEventCreateContext, ScheduleEventUpdateContext, ResourceStatus, ResourceSpec, MoleculeData, StorageCondition, ReagentColumn, Reagent, TreeNodeType, BadgeVariant, TreeNode, } from './components';
2
2
  export type { AuthConfig, UserInfo, LoginResponse, TokenVerifyResponse, RegisterRequest, UpdateProfileRequest, CredentialInfo, } from './auth';
3
3
  export type { PluginInfo, PluginNavItem, PluginSettings, PluginSettingField, PlatformContext, PlatformContextOptions, PlatformEventType, PlatformEvent, ThemeMode, ColorPalette, TableDensity, } from './platform';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morscherlab/mld-sdk",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "MLD Platform SDK - Vue 3 components, composables, and types for plugin development",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,17 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref } from 'vue'
3
-
4
- type AuditEntryType = 'create' | 'update' | 'delete' | 'system'
5
-
6
- interface AuditEntry {
7
- id: string
8
- type: AuditEntryType
9
- action: string
10
- detail?: string
11
- user?: string
12
- timestamp: Date | string
13
- metadata?: Record<string, unknown>
14
- }
3
+ import type { AuditEntryType, AuditEntry } from '../types'
15
4
 
16
5
  interface Props {
17
6
  entries: AuditEntry[]
@@ -1,25 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, watch, nextTick } from 'vue'
3
-
4
- type BatchItemStatus = 'pending' | 'processing' | 'completed' | 'error' | 'skipped'
5
-
6
- interface BatchItem {
7
- id: string
8
- label: string
9
- status: BatchItemStatus
10
- progress?: number
11
- message?: string
12
- }
13
-
14
- interface BatchSummary {
15
- total: number
16
- completed: number
17
- processing: number
18
- error: number
19
- pending: number
20
- skipped: number
21
- percent: number
22
- }
3
+ import type { BatchItem, BatchSummary } from '../types'
23
4
 
24
5
  interface Props {
25
6
  items: BatchItem[]
@@ -1,9 +1,5 @@
1
1
  <script setup lang="ts">
2
- interface BreadcrumbItem {
3
- label: string
4
- to?: string
5
- href?: string
6
- }
2
+ import type { BreadcrumbItem } from '../types'
7
3
 
8
4
  interface Props {
9
5
  items: BreadcrumbItem[]
@@ -370,7 +370,7 @@ onUnmounted(() => {
370
370
  <div class="mld-datetime-picker__footer">
371
371
  <div>
372
372
  <button type="button" class="mld-datetime-picker__footer-btn" @click="goToToday">Today</button>
373
- <button type="button" class="mld-datetime-picker__footer-btn" style="margin-left: 0.75rem" @click="goToNow">Now</button>
373
+ <button type="button" class="mld-datetime-picker__footer-btn" @click="goToNow">Now</button>
374
374
  </div>
375
375
  <button
376
376
  v-if="clearable && modelValue"
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import MoleculeInput from './MoleculeInput.vue'
3
- import type { MoleculeData } from './MoleculeInput.vue'
3
+ import type { MoleculeData } from '../types'
4
4
 
5
5
  function initState() {
6
6
  return {
@@ -1,10 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
3
-
4
- export interface MoleculeData {
5
- smiles: string
6
- molfile: string
7
- }
3
+ import type { MoleculeData } from '../types'
8
4
 
9
5
  interface Props {
10
6
  modelValue?: MoleculeData
@@ -287,7 +287,7 @@ const activeRackWells = computed(() => editor.activeRack.value?.wells ?? {})
287
287
  <div v-if="editor.activeRack.value && !readonly" class="mld-rack-editor__toolbar">
288
288
  <div class="mld-rack-editor__toolbar-group">
289
289
  <!-- Format selector -->
290
- <div style="display: flex; align-items: center; gap: 0.5rem;">
290
+ <div class="mld-rack-editor__toolbar-section">
291
291
  <span class="mld-rack-editor__toolbar-label">Plate</span>
292
292
  <div class="mld-rack-editor__format-btns">
293
293
  <button
@@ -307,7 +307,7 @@ const activeRackWells = computed(() => editor.activeRack.value?.wells ?? {})
307
307
  <div class="mld-rack-editor__toolbar-divider" />
308
308
 
309
309
  <!-- Slot selector -->
310
- <div style="display: flex; align-items: center; gap: 0.5rem;">
310
+ <div class="mld-rack-editor__toolbar-section">
311
311
  <span class="mld-rack-editor__toolbar-label">Slot</span>
312
312
  <div class="mld-rack-editor__slot-btns">
313
313
  <button