@morscherlab/mld-sdk 0.6.1 → 0.6.3
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/ConcentrationInput.vue.js +17 -16
- package/dist/components/ConcentrationInput.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/NumberInput.vue.js +1 -0
- package/dist/components/NumberInput.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 +19 -18
- package/dist/components/UnitInput.vue.js.map +1 -1
- package/dist/components/WellPlate.vue.js +23 -18
- package/dist/components/WellPlate.vue.js.map +1 -1
- package/dist/styles.css +163 -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/ConcentrationInput.vue +1 -1
- 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/NumberInput.vue +1 -0
- 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 +2 -8
- package/src/components/WellPlate.vue +26 -19
- package/src/styles/components/button.css +6 -0
- package/src/styles/components/concentration-input.css +14 -0
- package/src/styles/components/date-picker.css +5 -0
- package/src/styles/components/datetime-picker.css +4 -0
- package/src/styles/components/dropdown-button.css +5 -0
- package/src/styles/components/icon-button.css +4 -0
- package/src/styles/components/input.css +5 -0
- package/src/styles/components/number-input.css +14 -0
- package/src/styles/components/rack-editor.css +6 -0
- package/src/styles/components/segmented-control.css +4 -0
- package/src/styles/components/select.css +5 -0
- package/src/styles/components/tags-input.css +4 -0
- package/src/styles/components/unit-input.css +14 -0
- package/src/styles/variables.css +5 -0
- package/src/types/index.ts +10 -0
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
interface AuditEntry {
|
|
3
|
-
id: string;
|
|
4
|
-
type: AuditEntryType;
|
|
5
|
-
action: string;
|
|
6
|
-
detail?: string;
|
|
7
|
-
user?: string;
|
|
8
|
-
timestamp: Date | string;
|
|
9
|
-
metadata?: Record<string, unknown>;
|
|
10
|
-
}
|
|
1
|
+
import { AuditEntry } from '../types';
|
|
11
2
|
interface Props {
|
|
12
3
|
entries: AuditEntry[];
|
|
13
4
|
maxHeight?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuditTrail.vue.js","sources":["../../src/components/AuditTrail.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\
|
|
1
|
+
{"version":3,"file":"AuditTrail.vue.js","sources":["../../src/components/AuditTrail.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport type { AuditEntryType, AuditEntry } from '../types'\n\ninterface Props {\n entries: AuditEntry[]\n maxHeight?: string\n showFilters?: boolean\n emptyMessage?: string\n order?: 'newest' | 'oldest'\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showFilters: false,\n emptyMessage: 'No activity yet',\n order: 'newest',\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'entry-click': [entry: AuditEntry]\n}>()\n\nconst filterType = ref<AuditEntryType | ''>('')\nconst filterUser = ref('')\n\nconst uniqueTypes = computed(() => {\n const types = new Set(props.entries.map(e => e.type))\n return Array.from(types)\n})\n\nconst uniqueUsers = computed(() => {\n const users = new Set(props.entries.filter(e => e.user).map(e => e.user!))\n return Array.from(users)\n})\n\nconst filteredEntries = computed(() => {\n let result = props.entries\n if (filterType.value) {\n result = result.filter(e => e.type === filterType.value)\n }\n if (filterUser.value) {\n result = result.filter(e => e.user === filterUser.value)\n }\n return result\n})\n\nconst sortedEntries = computed(() => {\n const entries = [...filteredEntries.value]\n entries.sort((a, b) => {\n const timeA = new Date(a.timestamp).getTime()\n const timeB = new Date(b.timestamp).getTime()\n return props.order === 'newest' ? timeB - timeA : timeA - timeB\n })\n return entries\n})\n\nfunction getInitials(name: string): string {\n const words = name.trim().split(/\\s+/)\n if (words.length >= 2) {\n return (words[0][0] + words[1][0]).toUpperCase()\n }\n return (words[0]?.[0] ?? '').toUpperCase()\n}\n\nconst rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })\n\nfunction formatTimestamp(timestamp: Date | string): string {\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp)\n const now = Date.now()\n const diffMs = date.getTime() - now\n const diffSec = Math.round(diffMs / 1000)\n const diffMin = Math.round(diffSec / 60)\n const diffHr = Math.round(diffMin / 60)\n const diffDay = Math.round(diffHr / 24)\n const diffWeek = Math.round(diffDay / 7)\n\n if (Math.abs(diffSec) < 60) return rtf.format(diffSec, 'second')\n if (Math.abs(diffMin) < 60) return rtf.format(diffMin, 'minute')\n if (Math.abs(diffHr) < 24) return rtf.format(diffHr, 'hour')\n if (Math.abs(diffDay) < 7) return rtf.format(diffDay, 'day')\n if (Math.abs(diffWeek) < 5) return rtf.format(diffWeek, 'week')\n return date.toLocaleDateString()\n}\n\nfunction handleEntryClick(entry: AuditEntry) {\n emit('entry-click', entry)\n}\n</script>\n\n<template>\n <div :class=\"['mld-audit-trail', `mld-audit-trail--${size}`]\">\n <div v-if=\"showFilters\" class=\"mld-audit-trail__filters\">\n <select v-model=\"filterType\" class=\"mld-audit-trail__filter-select\">\n <option value=\"\">All types</option>\n <option v-for=\"t in uniqueTypes\" :key=\"t\" :value=\"t\">{{ t }}</option>\n </select>\n <select v-model=\"filterUser\" class=\"mld-audit-trail__filter-select\">\n <option value=\"\">All users</option>\n <option v-for=\"u in uniqueUsers\" :key=\"u\" :value=\"u\">{{ u }}</option>\n </select>\n </div>\n\n <div\n v-if=\"sortedEntries.length\"\n class=\"mld-audit-trail__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div class=\"mld-audit-trail__line\" />\n <div\n v-for=\"entry in sortedEntries\"\n :key=\"entry.id\"\n :class=\"['mld-audit-trail__entry', `mld-audit-trail__entry--${entry.type}`]\"\n @click=\"handleEntryClick(entry)\"\n >\n <slot name=\"entry\" :entry=\"entry\">\n <div class=\"mld-audit-trail__dot\" />\n <div class=\"mld-audit-trail__content\">\n <div class=\"mld-audit-trail__header\">\n <div v-if=\"entry.user\" class=\"mld-audit-trail__avatar\">{{ getInitials(entry.user) }}</div>\n <span v-if=\"entry.user\" class=\"mld-audit-trail__user\">{{ entry.user }}</span>\n <span class=\"mld-audit-trail__timestamp\">{{ formatTimestamp(entry.timestamp) }}</span>\n </div>\n <div class=\"mld-audit-trail__action\">{{ entry.action }}</div>\n <div v-if=\"entry.detail\" class=\"mld-audit-trail__detail\">{{ entry.detail }}</div>\n <div v-if=\"entry.metadata && Object.keys(entry.metadata).length\" class=\"mld-audit-trail__metadata\">\n <span\n v-for=\"(value, key) in entry.metadata\"\n :key=\"String(key)\"\n class=\"mld-audit-trail__metadata-item\"\n >\n <span class=\"mld-audit-trail__metadata-key\">{{ key }}:</span>\n {{ value }}\n </span>\n </div>\n </div>\n </slot>\n </div>\n </div>\n\n <div v-if=\"!sortedEntries.length\" class=\"mld-audit-trail__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/audit-trail.css';\n</style>\n"],"names":["_createElementBlock","_openBlock","_createElementVNode","_Fragment","_renderList","_normalizeStyle","_normalizeClass","_renderSlot","_toDisplayString","_createTextVNode"],"mappings":"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"}
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
interface BatchItem {
|
|
3
|
-
id: string;
|
|
4
|
-
label: string;
|
|
5
|
-
status: BatchItemStatus;
|
|
6
|
-
progress?: number;
|
|
7
|
-
message?: string;
|
|
8
|
-
}
|
|
9
|
-
interface BatchSummary {
|
|
10
|
-
total: number;
|
|
11
|
-
completed: number;
|
|
12
|
-
processing: number;
|
|
13
|
-
error: number;
|
|
14
|
-
pending: number;
|
|
15
|
-
skipped: number;
|
|
16
|
-
percent: number;
|
|
17
|
-
}
|
|
1
|
+
import { BatchItem, BatchSummary } from '../types';
|
|
18
2
|
interface Props {
|
|
19
3
|
items: BatchItem[];
|
|
20
4
|
showSummary?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BatchProgressList.vue.js","sources":["../../src/components/BatchProgressList.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, nextTick } from 'vue'\n\ntype BatchItemStatus = 'pending' | 'processing' | 'completed' | 'error' | 'skipped'\n\ninterface BatchItem {\n id: string\n label: string\n status: BatchItemStatus\n progress?: number\n message?: string\n}\n\ninterface BatchSummary {\n total: number\n completed: number\n processing: number\n error: number\n pending: number\n skipped: number\n percent: number\n}\n\ninterface Props {\n items: BatchItem[]\n showSummary?: boolean\n title?: string\n maxHeight?: string\n autoScroll?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSummary: true,\n autoScroll: true,\n})\n\nconst emit = defineEmits<{\n 'retry': [id: string]\n 'cancel': [id: string]\n}>()\n\nconst listRef = ref<HTMLElement | null>(null)\nconst expandedErrors = ref<Set<string>>(new Set())\n\nconst summary = computed<BatchSummary>(() => {\n const total = props.items.length\n const completed = props.items.filter(i => i.status === 'completed').length\n const processing = props.items.filter(i => i.status === 'processing').length\n const error = props.items.filter(i => i.status === 'error').length\n const pending = props.items.filter(i => i.status === 'pending').length\n const skipped = props.items.filter(i => i.status === 'skipped').length\n const percent = total > 0 ? Math.round(((completed + skipped) / total) * 100) : 0\n return { total, completed, processing, error, pending, skipped, percent }\n})\n\nfunction toggleError(id: string) {\n if (expandedErrors.value.has(id)) {\n expandedErrors.value.delete(id)\n } else {\n expandedErrors.value.add(id)\n }\n}\n\nwatch(\n () => props.items.map(i => i.status),\n (newStatuses, oldStatuses) => {\n if (!props.autoScroll || !listRef.value) return\n for (let i = 0; i < newStatuses.length; i++) {\n if (newStatuses[i] === 'processing' && oldStatuses?.[i] !== 'processing') {\n nextTick(() => {\n const el = listRef.value?.querySelector(`[data-item-id=\"${props.items[i].id}\"]`)\n el?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n break\n }\n }\n },\n)\n</script>\n\n<template>\n <div class=\"mld-batch-progress\">\n <div v-if=\"title\" class=\"mld-batch-progress__header\">\n <span class=\"mld-batch-progress__title\">{{ title }}</span>\n <span class=\"mld-batch-progress__percent\">{{ summary.percent }}%</span>\n </div>\n\n <div class=\"mld-batch-progress__overall\">\n <div\n class=\"mld-batch-progress__overall-bar\"\n :style=\"{ width: `${summary.percent}%` }\"\n />\n </div>\n\n <slot v-if=\"showSummary\" name=\"summary\" :summary=\"summary\">\n <div class=\"mld-batch-progress__summary\">\n <span v-if=\"summary.completed\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--completed\">\n {{ summary.completed }} completed\n </span>\n <span v-if=\"summary.processing\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--processing\">\n {{ summary.processing }} processing\n </span>\n <span v-if=\"summary.error\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--error\">\n {{ summary.error }} failed\n </span>\n <span v-if=\"summary.pending\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--pending\">\n {{ summary.pending }} pending\n </span>\n <span v-if=\"summary.skipped\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--skipped\">\n {{ summary.skipped }} skipped\n </span>\n </div>\n </slot>\n\n <div\n ref=\"listRef\"\n class=\"mld-batch-progress__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div\n v-for=\"item in items\"\n :key=\"item.id\"\n :data-item-id=\"item.id\"\n :class=\"['mld-batch-progress__item', `mld-batch-progress__item--${item.status}`]\"\n >\n <slot name=\"item\" :item=\"item\">\n <div class=\"mld-batch-progress__item-row\">\n <!-- Status icon -->\n <div :class=\"['mld-batch-progress__item-icon', `mld-batch-progress__item-icon--${item.status}`]\">\n <!-- Pending: clock -->\n <svg v-if=\"item.status === 'pending'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" />\n <path d=\"M8 4.5V8l2.5 1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Processing: spinner -->\n <svg v-else-if=\"item.status === 'processing'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M8 1.5A6.5 6.5 0 1 1 1.5 8\" stroke-linecap=\"round\" />\n </svg>\n <!-- Completed: checkmark -->\n <svg v-else-if=\"item.status === 'completed'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M3.5 8.5L6.5 11.5L12.5 4.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Error: x -->\n <svg v-else-if=\"item.status === 'error'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4.5 4.5l7 7M11.5 4.5l-7 7\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Skipped: dash -->\n <svg v-else viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4 8h8\" stroke-linecap=\"round\" />\n </svg>\n </div>\n\n <span class=\"mld-batch-progress__item-label\">{{ item.label }}</span>\n\n <!-- Mini progress bar for processing items -->\n <div v-if=\"item.status === 'processing' && item.progress !== undefined\" class=\"mld-batch-progress__item-progress\">\n <div class=\"mld-batch-progress__item-progress-bar\" :style=\"{ width: `${item.progress}%` }\" />\n </div>\n\n <!-- Actions -->\n <div class=\"mld-batch-progress__item-actions\">\n <button\n v-if=\"item.status === 'error'\"\n type=\"button\"\n class=\"mld-batch-progress__retry-btn\"\n @click.stop=\"emit('retry', item.id)\"\n >\n Retry\n </button>\n <button\n v-if=\"item.status === 'processing'\"\n type=\"button\"\n class=\"mld-batch-progress__cancel-btn\"\n @click.stop=\"emit('cancel', item.id)\"\n >\n Cancel\n </button>\n </div>\n </div>\n\n <!-- Error message (expandable) -->\n <div v-if=\"item.status === 'error' && item.message\" class=\"mld-batch-progress__item-error\">\n <button\n type=\"button\"\n class=\"mld-batch-progress__error-toggle\"\n @click.stop=\"toggleError(item.id)\"\n >\n {{ expandedErrors.has(item.id) ? 'Hide error' : 'Show error' }}\n </button>\n <div v-if=\"expandedErrors.has(item.id)\" class=\"mld-batch-progress__item-message\">\n {{ item.message }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/batch-progress-list.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_normalizeStyle","_renderSlot","_Fragment","_renderList","_normalizeClass","_withModifiers"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BatchProgressList.vue.js","sources":["../../src/components/BatchProgressList.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, nextTick } from 'vue'\nimport type { BatchItem, BatchSummary } from '../types'\n\ninterface Props {\n items: BatchItem[]\n showSummary?: boolean\n title?: string\n maxHeight?: string\n autoScroll?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSummary: true,\n autoScroll: true,\n})\n\nconst emit = defineEmits<{\n 'retry': [id: string]\n 'cancel': [id: string]\n}>()\n\nconst listRef = ref<HTMLElement | null>(null)\nconst expandedErrors = ref<Set<string>>(new Set())\n\nconst summary = computed<BatchSummary>(() => {\n const total = props.items.length\n const completed = props.items.filter(i => i.status === 'completed').length\n const processing = props.items.filter(i => i.status === 'processing').length\n const error = props.items.filter(i => i.status === 'error').length\n const pending = props.items.filter(i => i.status === 'pending').length\n const skipped = props.items.filter(i => i.status === 'skipped').length\n const percent = total > 0 ? Math.round(((completed + skipped) / total) * 100) : 0\n return { total, completed, processing, error, pending, skipped, percent }\n})\n\nfunction toggleError(id: string) {\n if (expandedErrors.value.has(id)) {\n expandedErrors.value.delete(id)\n } else {\n expandedErrors.value.add(id)\n }\n}\n\nwatch(\n () => props.items.map(i => i.status),\n (newStatuses, oldStatuses) => {\n if (!props.autoScroll || !listRef.value) return\n for (let i = 0; i < newStatuses.length; i++) {\n if (newStatuses[i] === 'processing' && oldStatuses?.[i] !== 'processing') {\n nextTick(() => {\n const el = listRef.value?.querySelector(`[data-item-id=\"${props.items[i].id}\"]`)\n el?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n break\n }\n }\n },\n)\n</script>\n\n<template>\n <div class=\"mld-batch-progress\">\n <div v-if=\"title\" class=\"mld-batch-progress__header\">\n <span class=\"mld-batch-progress__title\">{{ title }}</span>\n <span class=\"mld-batch-progress__percent\">{{ summary.percent }}%</span>\n </div>\n\n <div class=\"mld-batch-progress__overall\">\n <div\n class=\"mld-batch-progress__overall-bar\"\n :style=\"{ width: `${summary.percent}%` }\"\n />\n </div>\n\n <slot v-if=\"showSummary\" name=\"summary\" :summary=\"summary\">\n <div class=\"mld-batch-progress__summary\">\n <span v-if=\"summary.completed\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--completed\">\n {{ summary.completed }} completed\n </span>\n <span v-if=\"summary.processing\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--processing\">\n {{ summary.processing }} processing\n </span>\n <span v-if=\"summary.error\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--error\">\n {{ summary.error }} failed\n </span>\n <span v-if=\"summary.pending\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--pending\">\n {{ summary.pending }} pending\n </span>\n <span v-if=\"summary.skipped\" class=\"mld-batch-progress__summary-item mld-batch-progress__summary-item--skipped\">\n {{ summary.skipped }} skipped\n </span>\n </div>\n </slot>\n\n <div\n ref=\"listRef\"\n class=\"mld-batch-progress__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div\n v-for=\"item in items\"\n :key=\"item.id\"\n :data-item-id=\"item.id\"\n :class=\"['mld-batch-progress__item', `mld-batch-progress__item--${item.status}`]\"\n >\n <slot name=\"item\" :item=\"item\">\n <div class=\"mld-batch-progress__item-row\">\n <!-- Status icon -->\n <div :class=\"['mld-batch-progress__item-icon', `mld-batch-progress__item-icon--${item.status}`]\">\n <!-- Pending: clock -->\n <svg v-if=\"item.status === 'pending'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" />\n <path d=\"M8 4.5V8l2.5 1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Processing: spinner -->\n <svg v-else-if=\"item.status === 'processing'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M8 1.5A6.5 6.5 0 1 1 1.5 8\" stroke-linecap=\"round\" />\n </svg>\n <!-- Completed: checkmark -->\n <svg v-else-if=\"item.status === 'completed'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M3.5 8.5L6.5 11.5L12.5 4.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Error: x -->\n <svg v-else-if=\"item.status === 'error'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4.5 4.5l7 7M11.5 4.5l-7 7\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Skipped: dash -->\n <svg v-else viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4 8h8\" stroke-linecap=\"round\" />\n </svg>\n </div>\n\n <span class=\"mld-batch-progress__item-label\">{{ item.label }}</span>\n\n <!-- Mini progress bar for processing items -->\n <div v-if=\"item.status === 'processing' && item.progress !== undefined\" class=\"mld-batch-progress__item-progress\">\n <div class=\"mld-batch-progress__item-progress-bar\" :style=\"{ width: `${item.progress}%` }\" />\n </div>\n\n <!-- Actions -->\n <div class=\"mld-batch-progress__item-actions\">\n <button\n v-if=\"item.status === 'error'\"\n type=\"button\"\n class=\"mld-batch-progress__retry-btn\"\n @click.stop=\"emit('retry', item.id)\"\n >\n Retry\n </button>\n <button\n v-if=\"item.status === 'processing'\"\n type=\"button\"\n class=\"mld-batch-progress__cancel-btn\"\n @click.stop=\"emit('cancel', item.id)\"\n >\n Cancel\n </button>\n </div>\n </div>\n\n <!-- Error message (expandable) -->\n <div v-if=\"item.status === 'error' && item.message\" class=\"mld-batch-progress__item-error\">\n <button\n type=\"button\"\n class=\"mld-batch-progress__error-toggle\"\n @click.stop=\"toggleError(item.id)\"\n >\n {{ expandedErrors.has(item.id) ? 'Hide error' : 'Show error' }}\n </button>\n <div v-if=\"expandedErrors.has(item.id)\" class=\"mld-batch-progress__item-message\">\n {{ item.message }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/batch-progress-list.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_normalizeStyle","_renderSlot","_Fragment","_renderList","_normalizeClass","_withModifiers"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Breadcrumb.vue.js","sources":["../../src/components/Breadcrumb.vue"],"sourcesContent":["<script setup lang=\"ts\">\
|
|
1
|
+
{"version":3,"file":"Breadcrumb.vue.js","sources":["../../src/components/Breadcrumb.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { BreadcrumbItem } from '../types'\n\ninterface Props {\n items: BreadcrumbItem[]\n separator?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n separator: '/',\n})\n\nconst emit = defineEmits<{\n navigate: [item: BreadcrumbItem]\n}>()\n\nfunction handleClick(item: BreadcrumbItem) {\n if (!item.href) {\n emit('navigate', item)\n }\n}\n</script>\n\n<template>\n <nav class=\"mld-breadcrumb\" aria-label=\"Breadcrumb\">\n <ol class=\"mld-breadcrumb__list\">\n <li\n v-for=\"(item, index) in items\"\n :key=\"index\"\n class=\"mld-breadcrumb__item\"\n >\n <slot name=\"item\" :item=\"item\" :index=\"index\" :is-last=\"index === items.length - 1\">\n <a\n v-if=\"item.href && index !== items.length - 1\"\n :href=\"item.href\"\n class=\"mld-breadcrumb__link\"\n >\n {{ item.label }}\n </a>\n <button\n v-else-if=\"index !== items.length - 1\"\n class=\"mld-breadcrumb__link\"\n @click=\"handleClick(item)\"\n >\n {{ item.label }}\n </button>\n <span\n v-else\n class=\"mld-breadcrumb__current\"\n aria-current=\"page\"\n >\n {{ item.label }}\n </span>\n </slot>\n <span\n v-if=\"index !== items.length - 1\"\n class=\"mld-breadcrumb__separator\"\n aria-hidden=\"true\"\n >\n <slot name=\"separator\">{{ separator }}</slot>\n </span>\n </li>\n </ol>\n </nav>\n</template>\n\n<style>\n@import '../styles/components/breadcrumb.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_Fragment","_renderList","_renderSlot","_toDisplayString"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,UAAM,OAAO;AAIb,aAAS,YAAY,MAAsB;AACzC,UAAI,CAAC,KAAK,MAAM;AACd,aAAK,YAAY,IAAI;AAAA,MACvB;AAAA,IACF;;AAIE,aAAAA,UAAA,GAAAC,mBAuCM,OAvCN,YAuCM;AAAA,QAtCJC,mBAqCK,MArCL,YAqCK;AAAA,WApCHF,UAAA,IAAA,GAAAC,mBAmCKE,UAAA,MAAAC,WAlCqB,QAAA,OAAK,CAArB,MAAM,UAAK;gCADrBH,mBAmCK,MAAA;AAAA,cAjCF,KAAK;AAAA,cACN,OAAM;AAAA,YAAA;cAENI,WAsBO,KAAA,QAAA,QAAA;AAAA,gBAtBY;AAAA,gBAAa;AAAA,gBAAe,QAAS,UAAU,QAAA,MAAM,SAAM;AAAA,cAAA,GAA9E,MAsBO;AAAA,gBApBG,KAAK,QAAQ,UAAU,QAAA,MAAM,SAAM,kBAD3CJ,mBAMI,KAAA;AAAA;kBAJD,MAAM,KAAK;AAAA,kBACZ,OAAM;AAAA,gBAAA,GAEHK,gBAAA,KAAK,KAAK,GAAA,GAAA,UAAA,KAGF,UAAU,QAAA,MAAM,SAAM,kBADnCL,mBAMS,UAAA;AAAA;kBAJP,OAAM;AAAA,kBACL,SAAK,CAAA,WAAE,YAAY,IAAI;AAAA,gBAAA,GAErBK,gBAAA,KAAK,KAAK,GAAA,GAAA,UAAA,MAEfN,UAAA,GAAAC,mBAMO,QANP,YAMOK,gBADF,KAAK,KAAK,GAAA,CAAA;AAAA,cAAA;cAIT,UAAU,QAAA,MAAM,SAAM,KAD9BN,aAAAC,mBAMO,QANP,YAMO;AAAA,gBADLI,WAA6C,8BAA7C,MAA6C;AAAA,kDAAnB,QAAA,SAAS,GAAA,CAAA;AAAA,gBAAA;;;;;;;;;"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { defineComponent, computed, watch, openBlock, createElementBlock, normalizeClass, createElementVNode, Fragment, renderList, toDisplayString, createCommentVNode } from "vue";
|
|
2
2
|
import { useConcentrationUnits } from "../composables/useConcentrationUnits.js";
|
|
3
|
-
const _hoisted_1 =
|
|
4
|
-
const _hoisted_2 =
|
|
5
|
-
const _hoisted_3 =
|
|
6
|
-
const _hoisted_4 = ["
|
|
7
|
-
const _hoisted_5 = ["
|
|
3
|
+
const _hoisted_1 = ["value", "min", "max", "disabled", "placeholder"];
|
|
4
|
+
const _hoisted_2 = { class: "mld-concentration-input__unit" };
|
|
5
|
+
const _hoisted_3 = ["value", "disabled"];
|
|
6
|
+
const _hoisted_4 = ["label"];
|
|
7
|
+
const _hoisted_5 = ["value"];
|
|
8
8
|
const _hoisted_6 = ["value"];
|
|
9
|
-
const _hoisted_7 =
|
|
10
|
-
const _hoisted_8 = {
|
|
9
|
+
const _hoisted_7 = {
|
|
11
10
|
key: 0,
|
|
12
11
|
class: "mld-concentration-input__conversion"
|
|
13
12
|
};
|
|
@@ -97,7 +96,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
97
96
|
__props.disabled ? "mld-concentration-input--disabled" : ""
|
|
98
97
|
])
|
|
99
98
|
}, [
|
|
100
|
-
createElementVNode("div",
|
|
99
|
+
createElementVNode("div", {
|
|
100
|
+
class: normalizeClass(["mld-concentration-input__controls", `mld-concentration-input__controls--${__props.size}`])
|
|
101
|
+
}, [
|
|
101
102
|
createElementVNode("input", {
|
|
102
103
|
type: "number",
|
|
103
104
|
value: currentValue.value,
|
|
@@ -112,8 +113,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
112
113
|
]),
|
|
113
114
|
"aria-label": "Concentration value",
|
|
114
115
|
onInput: handleValueInput
|
|
115
|
-
}, null, 42,
|
|
116
|
-
createElementVNode("div",
|
|
116
|
+
}, null, 42, _hoisted_1),
|
|
117
|
+
createElementVNode("div", _hoisted_2, [
|
|
117
118
|
createElementVNode("select", {
|
|
118
119
|
value: currentUnit.value,
|
|
119
120
|
disabled: __props.disabled,
|
|
@@ -137,20 +138,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
137
138
|
return openBlock(), createElementBlock("option", {
|
|
138
139
|
key: unit,
|
|
139
140
|
value: unit
|
|
140
|
-
}, toDisplayString(unit), 9,
|
|
141
|
+
}, toDisplayString(unit), 9, _hoisted_5);
|
|
141
142
|
}), 128))
|
|
142
|
-
], 8,
|
|
143
|
+
], 8, _hoisted_4)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(category.units, (unit) => {
|
|
143
144
|
return openBlock(), createElementBlock("option", {
|
|
144
145
|
key: unit,
|
|
145
146
|
value: unit
|
|
146
|
-
}, toDisplayString(unit), 9,
|
|
147
|
+
}, toDisplayString(unit), 9, _hoisted_6);
|
|
147
148
|
}), 128))
|
|
148
149
|
], 64);
|
|
149
150
|
}), 128))
|
|
150
|
-
], 42,
|
|
151
|
+
], 42, _hoisted_3)
|
|
151
152
|
])
|
|
152
|
-
]),
|
|
153
|
-
__props.showConversion && conversionHint.value ? (openBlock(), createElementBlock("div",
|
|
153
|
+
], 2),
|
|
154
|
+
__props.showConversion && conversionHint.value ? (openBlock(), createElementBlock("div", _hoisted_7, toDisplayString(conversionHint.value), 1)) : createCommentVNode("", true)
|
|
154
155
|
], 2);
|
|
155
156
|
};
|
|
156
157
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConcentrationInput.vue.js","sources":["../../src/components/ConcentrationInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, watch } from 'vue'\nimport {\n useConcentrationUnits,\n type ConcentrationValue,\n type ConcentrationUnit,\n} from '../composables/useConcentrationUnits'\n\ninterface Props {\n modelValue?: ConcentrationValue\n allowedUnits?: ConcentrationUnit[]\n showConversion?: boolean\n molecularWeight?: number\n min?: number\n max?: number\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showConversion: true,\n disabled: false,\n error: false,\n size: 'md',\n placeholder: 'Enter value',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ConcentrationValue | undefined]\n}>()\n\nconst { unitCategories, getConversionHint } = useConcentrationUnits()\n\n// Filter categories based on allowedUnits\nconst filteredCategories = computed(() => {\n if (!props.allowedUnits || props.allowedUnits.length === 0) {\n return unitCategories.value\n }\n return unitCategories.value\n .map(cat => ({\n label: cat.label,\n units: cat.units.filter(u => props.allowedUnits!.includes(u)),\n }))\n .filter(cat => cat.units.length > 0)\n})\n\n// Flatten all available units\nconst availableUnits = computed(() => {\n return filteredCategories.value.flatMap(cat => cat.units)\n})\n\n// Current value and unit\nconst currentValue = computed(() => props.modelValue?.value)\nconst currentUnit = computed(() => props.modelValue?.unit || availableUnits.value[0] || 'µM')\n\n// Conversion hint\nconst conversionHint = computed(() => {\n if (!props.showConversion || !props.modelValue) return null\n return getConversionHint(props.modelValue)\n})\n\nfunction handleValueInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? undefined : Number(target.value)\n\n if (value === undefined || isNaN(value)) {\n emit('update:modelValue', undefined)\n return\n }\n\n // Apply min/max constraints\n let clampedValue = value\n if (props.min !== undefined && clampedValue < props.min) {\n clampedValue = props.min\n }\n if (props.max !== undefined && clampedValue > props.max) {\n clampedValue = props.max\n }\n\n emit('update:modelValue', {\n value: clampedValue,\n unit: currentUnit.value,\n })\n}\n\nfunction handleUnitChange(event: Event) {\n const target = event.target as HTMLSelectElement\n const unit = target.value as ConcentrationUnit\n\n emit('update:modelValue', {\n value: currentValue.value ?? 0,\n unit,\n })\n}\n\n// Ensure modelValue always has a valid unit\nwatch(availableUnits, (units) => {\n if (props.modelValue && !units.includes(props.modelValue.unit)) {\n emit('update:modelValue', {\n value: props.modelValue.value,\n unit: units[0] || 'µM',\n })\n }\n}, { immediate: true })\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-concentration-input',\n error ? 'mld-concentration-input--error' : '',\n disabled ? 'mld-concentration-input--disabled' : '',\n ]\"\n >\n <div class=\"mld-concentration-input__controls\">\n <input\n type=\"number\"\n :value=\"currentValue\"\n :min=\"min\"\n :max=\"max\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :class=\"[\n 'mld-concentration-input__value',\n `mld-concentration-input__value--${size}`,\n disabled ? 'mld-concentration-input__value--disabled' : '',\n ]\"\n aria-label=\"Concentration value\"\n @input=\"handleValueInput\"\n />\n\n <div class=\"mld-concentration-input__unit\">\n <select\n :value=\"currentUnit\"\n :disabled=\"disabled\"\n :class=\"[\n 'mld-concentration-input__unit-select',\n `mld-concentration-input__unit-select--${size}`,\n ]\"\n aria-label=\"Concentration unit\"\n @change=\"handleUnitChange\"\n >\n <template v-for=\"category in filteredCategories\" :key=\"category.label\">\n <optgroup\n v-if=\"filteredCategories.length > 1\"\n :label=\"category.label\"\n class=\"mld-concentration-input__unit-group\"\n >\n <option\n v-for=\"unit in category.units\"\n :key=\"unit\"\n :value=\"unit\"\n >\n {{ unit }}\n </option>\n </optgroup>\n <template v-else>\n <option\n v-for=\"unit in category.units\"\n :key=\"unit\"\n :value=\"unit\"\n >\n {{ unit }}\n </option>\n </template>\n </template>\n </select>\n </div>\n </div>\n\n <div\n v-if=\"showConversion && conversionHint\"\n class=\"mld-concentration-input__conversion\"\n >\n {{ conversionHint }}\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/concentration-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_Fragment","_renderList","_openBlock","_toDisplayString"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConcentrationInput.vue.js","sources":["../../src/components/ConcentrationInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, watch } from 'vue'\nimport {\n useConcentrationUnits,\n type ConcentrationValue,\n type ConcentrationUnit,\n} from '../composables/useConcentrationUnits'\n\ninterface Props {\n modelValue?: ConcentrationValue\n allowedUnits?: ConcentrationUnit[]\n showConversion?: boolean\n molecularWeight?: number\n min?: number\n max?: number\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showConversion: true,\n disabled: false,\n error: false,\n size: 'md',\n placeholder: 'Enter value',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ConcentrationValue | undefined]\n}>()\n\nconst { unitCategories, getConversionHint } = useConcentrationUnits()\n\n// Filter categories based on allowedUnits\nconst filteredCategories = computed(() => {\n if (!props.allowedUnits || props.allowedUnits.length === 0) {\n return unitCategories.value\n }\n return unitCategories.value\n .map(cat => ({\n label: cat.label,\n units: cat.units.filter(u => props.allowedUnits!.includes(u)),\n }))\n .filter(cat => cat.units.length > 0)\n})\n\n// Flatten all available units\nconst availableUnits = computed(() => {\n return filteredCategories.value.flatMap(cat => cat.units)\n})\n\n// Current value and unit\nconst currentValue = computed(() => props.modelValue?.value)\nconst currentUnit = computed(() => props.modelValue?.unit || availableUnits.value[0] || 'µM')\n\n// Conversion hint\nconst conversionHint = computed(() => {\n if (!props.showConversion || !props.modelValue) return null\n return getConversionHint(props.modelValue)\n})\n\nfunction handleValueInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? undefined : Number(target.value)\n\n if (value === undefined || isNaN(value)) {\n emit('update:modelValue', undefined)\n return\n }\n\n // Apply min/max constraints\n let clampedValue = value\n if (props.min !== undefined && clampedValue < props.min) {\n clampedValue = props.min\n }\n if (props.max !== undefined && clampedValue > props.max) {\n clampedValue = props.max\n }\n\n emit('update:modelValue', {\n value: clampedValue,\n unit: currentUnit.value,\n })\n}\n\nfunction handleUnitChange(event: Event) {\n const target = event.target as HTMLSelectElement\n const unit = target.value as ConcentrationUnit\n\n emit('update:modelValue', {\n value: currentValue.value ?? 0,\n unit,\n })\n}\n\n// Ensure modelValue always has a valid unit\nwatch(availableUnits, (units) => {\n if (props.modelValue && !units.includes(props.modelValue.unit)) {\n emit('update:modelValue', {\n value: props.modelValue.value,\n unit: units[0] || 'µM',\n })\n }\n}, { immediate: true })\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-concentration-input',\n error ? 'mld-concentration-input--error' : '',\n disabled ? 'mld-concentration-input--disabled' : '',\n ]\"\n >\n <div :class=\"['mld-concentration-input__controls', `mld-concentration-input__controls--${size}`]\">\n <input\n type=\"number\"\n :value=\"currentValue\"\n :min=\"min\"\n :max=\"max\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :class=\"[\n 'mld-concentration-input__value',\n `mld-concentration-input__value--${size}`,\n disabled ? 'mld-concentration-input__value--disabled' : '',\n ]\"\n aria-label=\"Concentration value\"\n @input=\"handleValueInput\"\n />\n\n <div class=\"mld-concentration-input__unit\">\n <select\n :value=\"currentUnit\"\n :disabled=\"disabled\"\n :class=\"[\n 'mld-concentration-input__unit-select',\n `mld-concentration-input__unit-select--${size}`,\n ]\"\n aria-label=\"Concentration unit\"\n @change=\"handleUnitChange\"\n >\n <template v-for=\"category in filteredCategories\" :key=\"category.label\">\n <optgroup\n v-if=\"filteredCategories.length > 1\"\n :label=\"category.label\"\n class=\"mld-concentration-input__unit-group\"\n >\n <option\n v-for=\"unit in category.units\"\n :key=\"unit\"\n :value=\"unit\"\n >\n {{ unit }}\n </option>\n </optgroup>\n <template v-else>\n <option\n v-for=\"unit in category.units\"\n :key=\"unit\"\n :value=\"unit\"\n >\n {{ unit }}\n </option>\n </template>\n </template>\n </select>\n </div>\n </div>\n\n <div\n v-if=\"showConversion && conversionHint\"\n class=\"mld-concentration-input__conversion\"\n >\n {{ conversionHint }}\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/concentration-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_Fragment","_renderList","_openBlock","_toDisplayString"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateTimePicker.vue.js","sources":["../../src/components/DateTimePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'\nimport { generateTimeSlots, formatTime, parseTime } from '../composables/useTimeUtils'\n\ninterface Props {\n modelValue?: string\n placeholder?: string\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n timeStep?: number\n timeFormat?: '12h' | '24h'\n clearable?: boolean\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n placeholder: 'Select date & time',\n disabled: false,\n error: false,\n size: 'md',\n timeStep: 15,\n timeFormat: '24h',\n clearable: false,\n locale: 'en-US',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n\nconst isOpen = ref(false)\nconst containerRef = ref<HTMLDivElement>()\n\n// Parse model value into date and time parts\nconst selectedDate = computed(() => {\n if (!props.modelValue) return null\n const d = new Date(props.modelValue)\n return isNaN(d.getTime()) ? null : d\n})\n\nconst selectedDateStr = computed(() => {\n if (!selectedDate.value) return null\n const y = selectedDate.value.getFullYear()\n const m = String(selectedDate.value.getMonth() + 1).padStart(2, '0')\n const d = String(selectedDate.value.getDate()).padStart(2, '0')\n return `${y}-${m}-${d}`\n})\n\nconst selectedTimeStr = computed(() => {\n if (!selectedDate.value) return null\n return formatTime(selectedDate.value.getHours(), selectedDate.value.getMinutes())\n})\n\nconst displayValue = computed(() => {\n if (!selectedDate.value) return ''\n const dateStr = selectedDate.value.toLocaleDateString(props.locale, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n const timeStr = formatTime(\n selectedDate.value.getHours(),\n selectedDate.value.getMinutes(),\n props.timeFormat,\n )\n return `${dateStr} ${timeStr}`\n})\n\n// Calendar state\nconst currentMonth = ref(new Date())\nconst weekDays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']\n\nconst calendarDays = computed(() => {\n const year = currentMonth.value.getFullYear()\n const month = currentMonth.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; isDisabled: boolean }[] = []\n\n const startPadding = firstDay.getDay()\n for (let i = startPadding - 1; i >= 0; i--) {\n const date = new Date(year, month, -i)\n days.push({ date, isCurrentMonth: false, isDisabled: isDateDisabled(date) })\n }\n\n for (let i = 1; i <= lastDay.getDate(); i++) {\n const date = new Date(year, month, i)\n days.push({ date, isCurrentMonth: true, isDisabled: isDateDisabled(date) })\n }\n\n const endPadding = 42 - days.length\n for (let i = 1; i <= endPadding; i++) {\n const date = new Date(year, month + 1, i)\n days.push({ date, isCurrentMonth: false, isDisabled: isDateDisabled(date) })\n }\n\n return days\n})\n\n// Time slots\nconst timeSlots = computed(() => {\n return generateTimeSlots('00:00', '23:59', props.timeStep)\n})\n\nconst monthYear = computed(() => {\n return currentMonth.value.toLocaleDateString(props.locale, {\n year: 'numeric',\n month: 'long',\n })\n})\n\nfunction isDateDisabled(date: Date): boolean {\n if (props.min) {\n const minDate = new Date(props.min)\n minDate.setHours(0, 0, 0, 0)\n const check = new Date(date)\n check.setHours(0, 0, 0, 0)\n if (check < minDate) return true\n }\n if (props.max) {\n const maxDate = new Date(props.max)\n maxDate.setHours(23, 59, 59, 999)\n const check = new Date(date)\n check.setHours(23, 59, 59, 999)\n if (check > maxDate) return true\n }\n return false\n}\n\nfunction isTimeDisabled(time: string): boolean {\n if (!selectedDateStr.value) return false\n const { hour, minute } = parseTime(time)\n\n if (props.min) {\n const minDate = new Date(props.min)\n if (selectedDateStr.value === formatDateStr(minDate)) {\n const minMin = minDate.getHours() * 60 + minDate.getMinutes()\n if (hour * 60 + minute < minMin) return true\n }\n }\n if (props.max) {\n const maxDate = new Date(props.max)\n if (selectedDateStr.value === formatDateStr(maxDate)) {\n const maxMin = maxDate.getHours() * 60 + maxDate.getMinutes()\n if (hour * 60 + minute > maxMin) return true\n }\n }\n return false\n}\n\nfunction formatDateStr(d: Date): string {\n const y = d.getFullYear()\n const m = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${y}-${m}-${day}`\n}\n\nfunction isSameDay(a: Date, b: Date | null): boolean {\n if (!b) return false\n return a.toDateString() === b.toDateString()\n}\n\nfunction isToday(date: Date): boolean {\n return date.toDateString() === new Date().toDateString()\n}\n\nfunction selectDate(day: { date: Date; isDisabled: boolean }) {\n if (day.isDisabled || props.disabled) return\n const dateStr = formatDateStr(day.date)\n const timeStr = selectedTimeStr.value || '09:00'\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n}\n\nfunction selectTime(time: string) {\n if (isTimeDisabled(time)) return\n const dateStr = selectedDateStr.value || formatDateStr(new Date())\n emit('update:modelValue', `${dateStr}T${time}`)\n}\n\nfunction prevMonth() {\n currentMonth.value = new Date(\n currentMonth.value.getFullYear(),\n currentMonth.value.getMonth() - 1,\n 1,\n )\n}\n\nfunction nextMonth() {\n currentMonth.value = new Date(\n currentMonth.value.getFullYear(),\n currentMonth.value.getMonth() + 1,\n 1,\n )\n}\n\nfunction goToNow() {\n const now = new Date()\n currentMonth.value = new Date(now.getFullYear(), now.getMonth(), 1)\n const dateStr = formatDateStr(now)\n const timeStr = formatTime(now.getHours(), now.getMinutes())\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n isOpen.value = false\n}\n\nfunction goToToday() {\n const today = new Date()\n currentMonth.value = new Date(today.getFullYear(), today.getMonth(), 1)\n const dateStr = formatDateStr(today)\n const timeStr = selectedTimeStr.value || '09:00'\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n}\n\nfunction clear() {\n emit('update:modelValue', undefined)\n isOpen.value = false\n}\n\nfunction toggleDropdown() {\n if (props.disabled) return\n isOpen.value = !isOpen.value\n}\n\nfunction handleClickOutside(event: MouseEvent) {\n if (containerRef.value && !containerRef.value.contains(event.target as Node)) {\n isOpen.value = false\n }\n}\n\nwatch(isOpen, (open) => {\n if (open && selectedDate.value) {\n currentMonth.value = new Date(\n selectedDate.value.getFullYear(),\n selectedDate.value.getMonth(),\n 1,\n )\n nextTick(() => {\n // Scroll time grid to selected time\n const grid = containerRef.value?.querySelector('.mld-datetime-picker__time-grid')\n const active = containerRef.value?.querySelector('.mld-datetime-picker__time-chip--active')\n if (grid && active) {\n active.scrollIntoView({ block: 'center' })\n }\n })\n }\n})\n\nonMounted(() => {\n document.addEventListener('click', handleClickOutside)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', handleClickOutside)\n})\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"mld-datetime-picker\">\n <div class=\"mld-datetime-picker__input-wrapper\">\n <div class=\"mld-datetime-picker__icon-calendar\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\" />\n </svg>\n </div>\n <input\n type=\"text\"\n readonly\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :class=\"[\n 'mld-datetime-picker__input',\n `mld-datetime-picker__input--${size}`,\n error ? 'mld-datetime-picker__input--error' : '',\n disabled ? 'mld-datetime-picker__input--disabled' : '',\n ]\"\n aria-label=\"Select date and time\"\n @click=\"toggleDropdown\"\n />\n <div class=\"mld-datetime-picker__icon-clock\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n </div>\n </div>\n\n <Transition\n enter-active-class=\"mld-datetime-picker__dropdown-enter-active\"\n enter-from-class=\"mld-datetime-picker__dropdown-enter-from\"\n enter-to-class=\"mld-datetime-picker__dropdown-enter-to\"\n leave-active-class=\"mld-datetime-picker__dropdown-leave-active\"\n leave-from-class=\"mld-datetime-picker__dropdown-leave-from\"\n leave-to-class=\"mld-datetime-picker__dropdown-leave-to\"\n >\n <div\n v-if=\"isOpen\"\n class=\"mld-datetime-picker__dropdown\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Date and time picker\"\n >\n <!-- Calendar section -->\n <div class=\"mld-datetime-picker__calendar-section\">\n <div class=\"mld-date-picker__header\">\n <button type=\"button\" class=\"mld-date-picker__nav-btn\" aria-label=\"Previous month\" @click=\"prevMonth\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n <span class=\"mld-date-picker__month-year\">{{ monthYear }}</span>\n <button type=\"button\" class=\"mld-date-picker__nav-btn\" aria-label=\"Next month\" @click=\"nextMonth\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </div>\n\n <div class=\"mld-date-picker__weekdays\">\n <div v-for=\"day in weekDays\" :key=\"day\" class=\"mld-date-picker__weekday\">{{ day }}</div>\n </div>\n\n <div class=\"mld-date-picker__grid\">\n <button\n v-for=\"(day, index) in calendarDays\"\n :key=\"index\"\n type=\"button\"\n :disabled=\"day.isDisabled\"\n :class=\"[\n 'mld-date-picker__day',\n !day.isCurrentMonth ? 'mld-date-picker__day--other-month' : '',\n day.isDisabled ? 'mld-date-picker__day--disabled' : '',\n isSameDay(day.date, selectedDate) ? 'mld-date-picker__day--selected' : '',\n isToday(day.date) && !isSameDay(day.date, selectedDate) ? 'mld-date-picker__day--today' : '',\n ]\"\n @click=\"selectDate(day)\"\n >\n {{ day.date.getDate() }}\n </button>\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"mld-datetime-picker__divider\" />\n\n <!-- Time section -->\n <div class=\"mld-datetime-picker__time-section\">\n <div class=\"mld-datetime-picker__time-label\">Time</div>\n <div class=\"mld-datetime-picker__time-grid\">\n <button\n v-for=\"time in timeSlots\"\n :key=\"time\"\n type=\"button\"\n :disabled=\"isTimeDisabled(time)\"\n :class=\"[\n 'mld-datetime-picker__time-chip',\n selectedTimeStr === time ? 'mld-datetime-picker__time-chip--active' : '',\n isTimeDisabled(time) ? 'mld-datetime-picker__time-chip--disabled' : '',\n ]\"\n @click=\"selectTime(time)\"\n >\n {{ timeFormat === '12h' ? formatTime(parseTime(time).hour, parseTime(time).minute, '12h') : time }}\n </button>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"mld-datetime-picker__footer\">\n <div>\n <button type=\"button\" class=\"mld-datetime-picker__footer-btn\" @click=\"goToToday\">Today</button>\n <button type=\"button\" class=\"mld-datetime-picker__footer-btn\" style=\"margin-left: 0.75rem\" @click=\"goToNow\">Now</button>\n </div>\n <button\n v-if=\"clearable && modelValue\"\n type=\"button\"\n class=\"mld-datetime-picker__footer-btn mld-datetime-picker__footer-btn--muted\"\n @click=\"clear\"\n >\n Clear\n </button>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<style>\n@import '../styles/components/datetime-picker.css';\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_normalizeClass","_createVNode","_Transition","_openBlock","_toDisplayString","_Fragment","_renderList","_unref"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DateTimePicker.vue.js","sources":["../../src/components/DateTimePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'\nimport { generateTimeSlots, formatTime, parseTime } from '../composables/useTimeUtils'\n\ninterface Props {\n modelValue?: string\n placeholder?: string\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n timeStep?: number\n timeFormat?: '12h' | '24h'\n clearable?: boolean\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n placeholder: 'Select date & time',\n disabled: false,\n error: false,\n size: 'md',\n timeStep: 15,\n timeFormat: '24h',\n clearable: false,\n locale: 'en-US',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n\nconst isOpen = ref(false)\nconst containerRef = ref<HTMLDivElement>()\n\n// Parse model value into date and time parts\nconst selectedDate = computed(() => {\n if (!props.modelValue) return null\n const d = new Date(props.modelValue)\n return isNaN(d.getTime()) ? null : d\n})\n\nconst selectedDateStr = computed(() => {\n if (!selectedDate.value) return null\n const y = selectedDate.value.getFullYear()\n const m = String(selectedDate.value.getMonth() + 1).padStart(2, '0')\n const d = String(selectedDate.value.getDate()).padStart(2, '0')\n return `${y}-${m}-${d}`\n})\n\nconst selectedTimeStr = computed(() => {\n if (!selectedDate.value) return null\n return formatTime(selectedDate.value.getHours(), selectedDate.value.getMinutes())\n})\n\nconst displayValue = computed(() => {\n if (!selectedDate.value) return ''\n const dateStr = selectedDate.value.toLocaleDateString(props.locale, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n const timeStr = formatTime(\n selectedDate.value.getHours(),\n selectedDate.value.getMinutes(),\n props.timeFormat,\n )\n return `${dateStr} ${timeStr}`\n})\n\n// Calendar state\nconst currentMonth = ref(new Date())\nconst weekDays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']\n\nconst calendarDays = computed(() => {\n const year = currentMonth.value.getFullYear()\n const month = currentMonth.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; isDisabled: boolean }[] = []\n\n const startPadding = firstDay.getDay()\n for (let i = startPadding - 1; i >= 0; i--) {\n const date = new Date(year, month, -i)\n days.push({ date, isCurrentMonth: false, isDisabled: isDateDisabled(date) })\n }\n\n for (let i = 1; i <= lastDay.getDate(); i++) {\n const date = new Date(year, month, i)\n days.push({ date, isCurrentMonth: true, isDisabled: isDateDisabled(date) })\n }\n\n const endPadding = 42 - days.length\n for (let i = 1; i <= endPadding; i++) {\n const date = new Date(year, month + 1, i)\n days.push({ date, isCurrentMonth: false, isDisabled: isDateDisabled(date) })\n }\n\n return days\n})\n\n// Time slots\nconst timeSlots = computed(() => {\n return generateTimeSlots('00:00', '23:59', props.timeStep)\n})\n\nconst monthYear = computed(() => {\n return currentMonth.value.toLocaleDateString(props.locale, {\n year: 'numeric',\n month: 'long',\n })\n})\n\nfunction isDateDisabled(date: Date): boolean {\n if (props.min) {\n const minDate = new Date(props.min)\n minDate.setHours(0, 0, 0, 0)\n const check = new Date(date)\n check.setHours(0, 0, 0, 0)\n if (check < minDate) return true\n }\n if (props.max) {\n const maxDate = new Date(props.max)\n maxDate.setHours(23, 59, 59, 999)\n const check = new Date(date)\n check.setHours(23, 59, 59, 999)\n if (check > maxDate) return true\n }\n return false\n}\n\nfunction isTimeDisabled(time: string): boolean {\n if (!selectedDateStr.value) return false\n const { hour, minute } = parseTime(time)\n\n if (props.min) {\n const minDate = new Date(props.min)\n if (selectedDateStr.value === formatDateStr(minDate)) {\n const minMin = minDate.getHours() * 60 + minDate.getMinutes()\n if (hour * 60 + minute < minMin) return true\n }\n }\n if (props.max) {\n const maxDate = new Date(props.max)\n if (selectedDateStr.value === formatDateStr(maxDate)) {\n const maxMin = maxDate.getHours() * 60 + maxDate.getMinutes()\n if (hour * 60 + minute > maxMin) return true\n }\n }\n return false\n}\n\nfunction formatDateStr(d: Date): string {\n const y = d.getFullYear()\n const m = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${y}-${m}-${day}`\n}\n\nfunction isSameDay(a: Date, b: Date | null): boolean {\n if (!b) return false\n return a.toDateString() === b.toDateString()\n}\n\nfunction isToday(date: Date): boolean {\n return date.toDateString() === new Date().toDateString()\n}\n\nfunction selectDate(day: { date: Date; isDisabled: boolean }) {\n if (day.isDisabled || props.disabled) return\n const dateStr = formatDateStr(day.date)\n const timeStr = selectedTimeStr.value || '09:00'\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n}\n\nfunction selectTime(time: string) {\n if (isTimeDisabled(time)) return\n const dateStr = selectedDateStr.value || formatDateStr(new Date())\n emit('update:modelValue', `${dateStr}T${time}`)\n}\n\nfunction prevMonth() {\n currentMonth.value = new Date(\n currentMonth.value.getFullYear(),\n currentMonth.value.getMonth() - 1,\n 1,\n )\n}\n\nfunction nextMonth() {\n currentMonth.value = new Date(\n currentMonth.value.getFullYear(),\n currentMonth.value.getMonth() + 1,\n 1,\n )\n}\n\nfunction goToNow() {\n const now = new Date()\n currentMonth.value = new Date(now.getFullYear(), now.getMonth(), 1)\n const dateStr = formatDateStr(now)\n const timeStr = formatTime(now.getHours(), now.getMinutes())\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n isOpen.value = false\n}\n\nfunction goToToday() {\n const today = new Date()\n currentMonth.value = new Date(today.getFullYear(), today.getMonth(), 1)\n const dateStr = formatDateStr(today)\n const timeStr = selectedTimeStr.value || '09:00'\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n}\n\nfunction clear() {\n emit('update:modelValue', undefined)\n isOpen.value = false\n}\n\nfunction toggleDropdown() {\n if (props.disabled) return\n isOpen.value = !isOpen.value\n}\n\nfunction handleClickOutside(event: MouseEvent) {\n if (containerRef.value && !containerRef.value.contains(event.target as Node)) {\n isOpen.value = false\n }\n}\n\nwatch(isOpen, (open) => {\n if (open && selectedDate.value) {\n currentMonth.value = new Date(\n selectedDate.value.getFullYear(),\n selectedDate.value.getMonth(),\n 1,\n )\n nextTick(() => {\n // Scroll time grid to selected time\n const grid = containerRef.value?.querySelector('.mld-datetime-picker__time-grid')\n const active = containerRef.value?.querySelector('.mld-datetime-picker__time-chip--active')\n if (grid && active) {\n active.scrollIntoView({ block: 'center' })\n }\n })\n }\n})\n\nonMounted(() => {\n document.addEventListener('click', handleClickOutside)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', handleClickOutside)\n})\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"mld-datetime-picker\">\n <div class=\"mld-datetime-picker__input-wrapper\">\n <div class=\"mld-datetime-picker__icon-calendar\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\" />\n </svg>\n </div>\n <input\n type=\"text\"\n readonly\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :class=\"[\n 'mld-datetime-picker__input',\n `mld-datetime-picker__input--${size}`,\n error ? 'mld-datetime-picker__input--error' : '',\n disabled ? 'mld-datetime-picker__input--disabled' : '',\n ]\"\n aria-label=\"Select date and time\"\n @click=\"toggleDropdown\"\n />\n <div class=\"mld-datetime-picker__icon-clock\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n </div>\n </div>\n\n <Transition\n enter-active-class=\"mld-datetime-picker__dropdown-enter-active\"\n enter-from-class=\"mld-datetime-picker__dropdown-enter-from\"\n enter-to-class=\"mld-datetime-picker__dropdown-enter-to\"\n leave-active-class=\"mld-datetime-picker__dropdown-leave-active\"\n leave-from-class=\"mld-datetime-picker__dropdown-leave-from\"\n leave-to-class=\"mld-datetime-picker__dropdown-leave-to\"\n >\n <div\n v-if=\"isOpen\"\n class=\"mld-datetime-picker__dropdown\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Date and time picker\"\n >\n <!-- Calendar section -->\n <div class=\"mld-datetime-picker__calendar-section\">\n <div class=\"mld-date-picker__header\">\n <button type=\"button\" class=\"mld-date-picker__nav-btn\" aria-label=\"Previous month\" @click=\"prevMonth\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n <span class=\"mld-date-picker__month-year\">{{ monthYear }}</span>\n <button type=\"button\" class=\"mld-date-picker__nav-btn\" aria-label=\"Next month\" @click=\"nextMonth\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </div>\n\n <div class=\"mld-date-picker__weekdays\">\n <div v-for=\"day in weekDays\" :key=\"day\" class=\"mld-date-picker__weekday\">{{ day }}</div>\n </div>\n\n <div class=\"mld-date-picker__grid\">\n <button\n v-for=\"(day, index) in calendarDays\"\n :key=\"index\"\n type=\"button\"\n :disabled=\"day.isDisabled\"\n :class=\"[\n 'mld-date-picker__day',\n !day.isCurrentMonth ? 'mld-date-picker__day--other-month' : '',\n day.isDisabled ? 'mld-date-picker__day--disabled' : '',\n isSameDay(day.date, selectedDate) ? 'mld-date-picker__day--selected' : '',\n isToday(day.date) && !isSameDay(day.date, selectedDate) ? 'mld-date-picker__day--today' : '',\n ]\"\n @click=\"selectDate(day)\"\n >\n {{ day.date.getDate() }}\n </button>\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"mld-datetime-picker__divider\" />\n\n <!-- Time section -->\n <div class=\"mld-datetime-picker__time-section\">\n <div class=\"mld-datetime-picker__time-label\">Time</div>\n <div class=\"mld-datetime-picker__time-grid\">\n <button\n v-for=\"time in timeSlots\"\n :key=\"time\"\n type=\"button\"\n :disabled=\"isTimeDisabled(time)\"\n :class=\"[\n 'mld-datetime-picker__time-chip',\n selectedTimeStr === time ? 'mld-datetime-picker__time-chip--active' : '',\n isTimeDisabled(time) ? 'mld-datetime-picker__time-chip--disabled' : '',\n ]\"\n @click=\"selectTime(time)\"\n >\n {{ timeFormat === '12h' ? formatTime(parseTime(time).hour, parseTime(time).minute, '12h') : time }}\n </button>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"mld-datetime-picker__footer\">\n <div>\n <button type=\"button\" class=\"mld-datetime-picker__footer-btn\" @click=\"goToToday\">Today</button>\n <button type=\"button\" class=\"mld-datetime-picker__footer-btn\" @click=\"goToNow\">Now</button>\n </div>\n <button\n v-if=\"clearable && modelValue\"\n type=\"button\"\n class=\"mld-datetime-picker__footer-btn mld-datetime-picker__footer-btn--muted\"\n @click=\"clear\"\n >\n Clear\n </button>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<style>\n@import '../styles/components/datetime-picker.css';\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_normalizeClass","_createVNode","_Transition","_openBlock","_toDisplayString","_Fragment","_renderList","_unref"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MoleculeInput.vue.js","sources":["../../src/components/MoleculeInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'\n\nexport interface MoleculeData {\n smiles: string\n molfile: string\n}\n\ninterface Props {\n modelValue?: MoleculeData\n disabled?: boolean\n readonly?: boolean\n height?: number\n showSmiles?: boolean\n placeholder?: string\n error?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n readonly: false,\n height: 300,\n showSmiles: true,\n placeholder: 'Draw a chemical structure',\n error: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [data: MoleculeData | undefined]\n 'error': [message: string]\n}>()\n\n// State\nconst containerRef = ref<HTMLDivElement | null>(null)\nconst jsmeInstance = ref<unknown>(null)\nconst isLoading = ref(true)\nconst loadError = ref<string | null>(null)\nconst debounceTimer = ref<ReturnType<typeof setTimeout> | null>(null)\n\n// Computed\nconst hasStructure = computed(() => {\n return props.modelValue?.smiles && props.modelValue.smiles.length > 0\n})\n\n// Use window-level state to persist across component instances and hot-reloads\ninterface JSMEGlobalState {\n __jsmeCallbacks__?: Array<() => void>\n __jsmeLoading__?: boolean\n __jsmeLoaded__?: boolean\n __jsmeLoadPromise__?: Promise<void>\n JSApplet?: { JSME?: new (id: string, width: string, height: string, options?: object) => unknown }\n jsmeOnLoad?: () => void\n}\n\nfunction getJSMEState(): JSMEGlobalState {\n const win = window as unknown as JSMEGlobalState\n if (!win.__jsmeCallbacks__) {\n win.__jsmeCallbacks__ = []\n }\n return win\n}\n\nfunction waitForJSME(): Promise<void> {\n const win = getJSMEState()\n\n // Already loaded\n if (win.JSApplet?.JSME) {\n return Promise.resolve()\n }\n\n // Reuse existing promise if loading\n if (win.__jsmeLoadPromise__) {\n return win.__jsmeLoadPromise__\n }\n\n win.__jsmeLoadPromise__ = new Promise((resolve, reject) => {\n // Double-check after promise creation\n if (win.JSApplet?.JSME) {\n resolve()\n return\n }\n\n // Set up global callback FIRST (before checking for existing script)\n const originalOnLoad = win.jsmeOnLoad\n win.jsmeOnLoad = () => {\n win.__jsmeLoaded__ = true\n originalOnLoad?.()\n win.__jsmeCallbacks__?.forEach(cb => cb())\n win.__jsmeCallbacks__ = []\n resolve()\n }\n\n // Add to callback queue\n win.__jsmeCallbacks__?.push(resolve)\n\n // Check if script already exists\n const existingScript = document.querySelector('script[data-jsme]')\n if (existingScript) {\n // Script exists, poll for JSApplet.JSME (in case jsmeOnLoad already fired)\n const checkReady = setInterval(() => {\n if (win.JSApplet?.JSME) {\n clearInterval(checkReady)\n win.__jsmeLoaded__ = true\n win.__jsmeCallbacks__?.forEach(cb => cb())\n win.__jsmeCallbacks__ = []\n resolve()\n }\n }, 100)\n\n setTimeout(() => {\n clearInterval(checkReady)\n if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {\n reject(new Error('JSME initialization timeout'))\n }\n }, 15000)\n return\n }\n\n // Load the script\n win.__jsmeLoading__ = true\n const script = document.createElement('script')\n script.src = 'https://jsme-editor.github.io/dist/jsme/jsme.nocache.js'\n script.integrity = 'sha384-l6tNzsc/eAJ7uql0dGAcHYI5ANVEV7DrJYjzXp3t13L+3OzLnfpzJO0Uio7mUSjY'\n script.crossOrigin = 'anonymous'\n script.async = true\n script.setAttribute('data-jsme', 'true')\n\n script.onerror = () => {\n win.__jsmeLoading__ = false\n win.__jsmeLoadPromise__ = undefined\n reject(new Error('Failed to load JSME script'))\n }\n\n // Timeout\n setTimeout(() => {\n if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {\n reject(new Error('JSME initialization timeout'))\n }\n }, 15000)\n\n document.head.appendChild(script)\n })\n\n return win.__jsmeLoadPromise__\n}\n\n// JSME initialization\nasync function initJSME() {\n if (!containerRef.value || props.readonly) return\n\n try {\n isLoading.value = true\n loadError.value = null\n\n // Wait for JSME to be ready\n await waitForJSME()\n\n // Wait for DOM to be ready\n await nextTick()\n\n if (!containerRef.value) return\n\n // Get JSME constructor from window\n const win = getJSMEState()\n\n if (!win.JSApplet?.JSME) {\n throw new Error('JSME library not available after loading')\n }\n\n // Create JSME instance\n const editorId = `jsme-${Date.now()}`\n containerRef.value.id = editorId\n\n const instance = new win.JSApplet.JSME(\n editorId,\n '100%',\n `${props.height}px`,\n {\n options: 'query,hydrogens,paste,depict',\n }\n )\n\n jsmeInstance.value = instance\n\n // Set initial value if provided\n if (props.modelValue?.molfile) {\n (instance as { readMolFile: (mol: string) => void }).readMolFile(props.modelValue.molfile)\n }\n\n // Set up change callback\n (instance as { setCallBack: (event: string, callback: () => void) => void }).setCallBack('AfterStructureModified', handleStructureChange)\n\n isLoading.value = false\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to load molecule editor'\n loadError.value = message\n emit('error', message)\n isLoading.value = false\n }\n}\n\nfunction handleStructureChange() {\n // Debounce the change event\n if (debounceTimer.value) {\n clearTimeout(debounceTimer.value)\n }\n\n debounceTimer.value = setTimeout(() => {\n if (!jsmeInstance.value) return\n\n const instance = jsmeInstance.value as {\n smiles: () => string\n molFile: () => string\n }\n\n const smiles = instance.smiles()\n const molfile = instance.molFile()\n\n if (!smiles || smiles.length === 0) {\n emit('update:modelValue', undefined)\n } else {\n emit('update:modelValue', { smiles, molfile })\n }\n }, 300)\n}\n\nfunction clearStructure() {\n if (jsmeInstance.value) {\n (jsmeInstance.value as { reset: () => void }).reset()\n }\n emit('update:modelValue', undefined)\n}\n\n// Watch for external value changes\nwatch(() => props.modelValue, (newValue) => {\n if (!jsmeInstance.value) return\n\n const instance = jsmeInstance.value as {\n smiles: () => string\n readMolFile: (mol: string) => void\n reset: () => void\n }\n\n // Only update if the external value differs from current\n const currentSmiles = instance.smiles()\n if (newValue?.smiles !== currentSmiles) {\n if (newValue?.molfile) {\n instance.readMolFile(newValue.molfile)\n } else {\n instance.reset()\n }\n }\n})\n\n// Lifecycle\nonMounted(() => {\n if (!props.readonly) {\n initJSME()\n } else {\n isLoading.value = false\n }\n})\n\nonUnmounted(() => {\n if (debounceTimer.value) {\n clearTimeout(debounceTimer.value)\n }\n jsmeInstance.value = null\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-molecule-input',\n disabled ? 'mld-molecule-input--disabled' : '',\n readonly ? 'mld-molecule-input--readonly' : '',\n error ? 'mld-molecule-input--error' : '',\n ]\"\n >\n <!-- Loading state -->\n <div\n v-if=\"isLoading && !readonly\"\n class=\"mld-molecule-input__skeleton\"\n :style=\"{ height: `${height}px` }\"\n >\n <svg\n class=\"mld-molecule-input__skeleton-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n <span class=\"mld-molecule-input__skeleton-text\">Loading molecule editor...</span>\n </div>\n\n <!-- Error state -->\n <div\n v-else-if=\"loadError\"\n class=\"mld-molecule-input__error\"\n >\n <svg\n class=\"mld-molecule-input__error-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n {{ loadError }}\n </div>\n\n <!-- Readonly mode - show empty or placeholder -->\n <template v-else-if=\"readonly\">\n <div\n v-if=\"hasStructure\"\n class=\"mld-molecule-input__readonly\"\n :style=\"{ height: `${height}px` }\"\n >\n <!-- In readonly mode, we just display a placeholder since we don't have SVG rendering -->\n <div class=\"mld-molecule-input__empty\">\n <svg\n class=\"mld-molecule-input__empty-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n <span class=\"mld-molecule-input__empty-text\">Structure defined (readonly)</span>\n </div>\n </div>\n <div\n v-else\n class=\"mld-molecule-input__empty\"\n :style=\"{ height: `${height}px` }\"\n >\n <svg\n class=\"mld-molecule-input__empty-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n <span class=\"mld-molecule-input__empty-text\">No structure</span>\n </div>\n </template>\n\n <!-- Editor mode -->\n <template v-else>\n <div\n ref=\"containerRef\"\n class=\"mld-molecule-input__editor\"\n :style=\"{ height: `${height}px` }\"\n role=\"application\"\n aria-label=\"Molecule structure editor\"\n />\n\n <!-- Actions toolbar -->\n <div class=\"mld-molecule-input__actions\">\n <button\n type=\"button\"\n class=\"mld-molecule-input__action-btn\"\n :disabled=\"!hasStructure || disabled\"\n aria-label=\"Clear structure\"\n @click=\"clearStructure\"\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 </div>\n </template>\n\n <!-- SMILES display -->\n <div\n v-if=\"showSmiles && hasStructure && !loadError\"\n class=\"mld-molecule-input__smiles\"\n >\n <span class=\"mld-molecule-input__smiles-label\">SMILES:</span>\n {{ modelValue?.smiles }}\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/molecule-input.css';\n</style>\n"],"names":["_a","_b","_createElementBlock","_normalizeClass","_createElementVNode","_openBlock","_createTextVNode","_Fragment","_toDisplayString"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAkBA,UAAM,QAAQ;AASd,UAAM,OAAO;AAMb,UAAM,eAAe,IAA2B,IAAI;AACpD,UAAM,eAAe,IAAa,IAAI;AACtC,UAAM,YAAY,IAAI,IAAI;AAC1B,UAAM,YAAY,IAAmB,IAAI;AACzC,UAAM,gBAAgB,IAA0C,IAAI;AAGpE,UAAM,eAAe,SAAS,MAAM;;AAClC,eAAO,WAAM,eAAN,mBAAkB,WAAU,MAAM,WAAW,OAAO,SAAS;AAAA,IACtE,CAAC;AAYD,aAAS,eAAgC;AACvC,YAAM,MAAM;AACZ,UAAI,CAAC,IAAI,mBAAmB;AAC1B,YAAI,oBAAoB,CAAA;AAAA,MAC1B;AACA,aAAO;AAAA,IACT;AAEA,aAAS,cAA6B;;AACpC,YAAM,MAAM,aAAA;AAGZ,WAAI,SAAI,aAAJ,mBAAc,MAAM;AACtB,eAAO,QAAQ,QAAA;AAAA,MACjB;AAGA,UAAI,IAAI,qBAAqB;AAC3B,eAAO,IAAI;AAAA,MACb;AAEA,UAAI,sBAAsB,IAAI,QAAQ,CAAC,SAAS,WAAW;;AAEzD,aAAIA,MAAA,IAAI,aAAJ,gBAAAA,IAAc,MAAM;AACtB,kBAAA;AACA;AAAA,QACF;AAGA,cAAM,iBAAiB,IAAI;AAC3B,YAAI,aAAa,MAAM;;AACrB,cAAI,iBAAiB;AACrB;AACA,WAAAA,MAAA,IAAI,sBAAJ,gBAAAA,IAAuB,QAAQ,CAAA,OAAM,GAAA;AACrC,cAAI,oBAAoB,CAAA;AACxB,kBAAA;AAAA,QACF;AAGA,kBAAI,sBAAJ,mBAAuB,KAAK;AAG5B,cAAM,iBAAiB,SAAS,cAAc,mBAAmB;AACjE,YAAI,gBAAgB;AAElB,gBAAM,aAAa,YAAY,MAAM;;AACnC,iBAAIA,MAAA,IAAI,aAAJ,gBAAAA,IAAc,MAAM;AACtB,4BAAc,UAAU;AACxB,kBAAI,iBAAiB;AACrB,eAAAC,MAAA,IAAI,sBAAJ,gBAAAA,IAAuB,QAAQ,CAAA,OAAM,GAAA;AACrC,kBAAI,oBAAoB,CAAA;AACxB,sBAAA;AAAA,YACF;AAAA,UACF,GAAG,GAAG;AAEN,qBAAW,MAAM;;AACf,0BAAc,UAAU;AACxB,gBAAI,CAAC,IAAI,kBAAkB,GAACD,MAAA,IAAI,aAAJ,gBAAAA,IAAc,OAAM;AAC9C,qBAAO,IAAI,MAAM,6BAA6B,CAAC;AAAA,YACjD;AAAA,UACF,GAAG,IAAK;AACR;AAAA,QACF;AAGA,YAAI,kBAAkB;AACtB,cAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,eAAO,MAAM;AACb,eAAO,YAAY;AACnB,eAAO,cAAc;AACrB,eAAO,QAAQ;AACf,eAAO,aAAa,aAAa,MAAM;AAEvC,eAAO,UAAU,MAAM;AACrB,cAAI,kBAAkB;AACtB,cAAI,sBAAsB;AAC1B,iBAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,QAChD;AAGA,mBAAW,MAAM;;AACf,cAAI,CAAC,IAAI,kBAAkB,GAACA,MAAA,IAAI,aAAJ,gBAAAA,IAAc,OAAM;AAC9C,mBAAO,IAAI,MAAM,6BAA6B,CAAC;AAAA,UACjD;AAAA,QACF,GAAG,IAAK;AAER,iBAAS,KAAK,YAAY,MAAM;AAAA,MAClC,CAAC;AAED,aAAO,IAAI;AAAA,IACb;AAGA,mBAAe,WAAW;;AACxB,UAAI,CAAC,aAAa,SAAS,MAAM,SAAU;AAE3C,UAAI;AACF,kBAAU,QAAQ;AAClB,kBAAU,QAAQ;AAGlB,cAAM,YAAA;AAGN,cAAM,SAAA;AAEN,YAAI,CAAC,aAAa,MAAO;AAGzB,cAAM,MAAM,aAAA;AAEZ,YAAI,GAAC,SAAI,aAAJ,mBAAc,OAAM;AACvB,gBAAM,IAAI,MAAM,0CAA0C;AAAA,QAC5D;AAGA,cAAM,WAAW,QAAQ,KAAK,IAAA,CAAK;AACnC,qBAAa,MAAM,KAAK;AAExB,cAAM,WAAW,IAAI,IAAI,SAAS;AAAA,UAChC;AAAA,UACA;AAAA,UACA,GAAG,MAAM,MAAM;AAAA,UACf;AAAA,YACE,SAAS;AAAA,UAAA;AAAA,QACX;AAGF,qBAAa,QAAQ;AAGrB,aAAI,WAAM,eAAN,mBAAkB,SAAS;AAC5B,mBAAoD,YAAY,MAAM,WAAW,OAAO;AAAA,QAC3F;AAGC,iBAA4E,YAAY,0BAA0B,qBAAqB;AAExI,kBAAU,QAAQ;AAAA,MACpB,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,kBAAU,QAAQ;AAClB,aAAK,SAAS,OAAO;AACrB,kBAAU,QAAQ;AAAA,MACpB;AAAA,IACF;AAEA,aAAS,wBAAwB;AAE/B,UAAI,cAAc,OAAO;AACvB,qBAAa,cAAc,KAAK;AAAA,MAClC;AAEA,oBAAc,QAAQ,WAAW,MAAM;AACrC,YAAI,CAAC,aAAa,MAAO;AAEzB,cAAM,WAAW,aAAa;AAK9B,cAAM,SAAS,SAAS,OAAA;AACxB,cAAM,UAAU,SAAS,QAAA;AAEzB,YAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,eAAK,qBAAqB,MAAS;AAAA,QACrC,OAAO;AACL,eAAK,qBAAqB,EAAE,QAAQ,QAAA,CAAS;AAAA,QAC/C;AAAA,MACF,GAAG,GAAG;AAAA,IACR;AAEA,aAAS,iBAAiB;AACxB,UAAI,aAAa,OAAO;AACrB,qBAAa,MAAgC,MAAA;AAAA,MAChD;AACA,WAAK,qBAAqB,MAAS;AAAA,IACrC;AAGA,UAAM,MAAM,MAAM,YAAY,CAAC,aAAa;AAC1C,UAAI,CAAC,aAAa,MAAO;AAEzB,YAAM,WAAW,aAAa;AAO9B,YAAM,gBAAgB,SAAS,OAAA;AAC/B,WAAI,qCAAU,YAAW,eAAe;AACtC,YAAI,qCAAU,SAAS;AACrB,mBAAS,YAAY,SAAS,OAAO;AAAA,QACvC,OAAO;AACL,mBAAS,MAAA;AAAA,QACX;AAAA,MACF;AAAA,IACF,CAAC;AAGD,cAAU,MAAM;AACd,UAAI,CAAC,MAAM,UAAU;AACnB,iBAAA;AAAA,MACF,OAAO;AACL,kBAAU,QAAQ;AAAA,MACpB;AAAA,IACF,CAAC;AAED,gBAAY,MAAM;AAChB,UAAI,cAAc,OAAO;AACvB,qBAAa,cAAc,KAAK;AAAA,MAClC;AACA,mBAAa,QAAQ;AAAA,IACvB,CAAC;;;0BAICE,mBAwIM,OAAA;AAAA,QAvIH,OAAKC,eAAA;AAAA;UAAsC,QAAA,WAAQ,iCAAA;AAAA,UAA8C,QAAA,WAAQ,iCAAA;AAAA,UAA8C,QAAA,QAAK,8BAAA;AAAA,QAAA;;QASrJ,UAAA,UAAc,QAAA,yBADtBD,mBAoBM,OAAA;AAAA;UAlBJ,OAAM;AAAA,UACL,mCAAoB,QAAA,MAAM,MAAA;AAAA,QAAA;UAE3BE,mBAaM,OAAA;AAAA,YAZJ,OAAM;AAAA,YACN,MAAK;AAAA,YACL,QAAO;AAAA,YACP,SAAQ;AAAA,YACR,eAAY;AAAA,UAAA;YAEZA,mBAKE,QAAA;AAAA,cAJA,kBAAe;AAAA,cACf,mBAAgB;AAAA,cAChB,gBAAa;AAAA,cACb,GAAE;AAAA,YAAA;;UAGNA,mBAAiF,QAAA,EAA3E,OAAM,oCAAA,GAAoC,8BAA0B,EAAA;AAAA,QAAA,WAK/D,UAAA,SADbC,aAAAH,mBAmBM,OAnBN,YAmBM;AAAA,oCAfJE,mBAaM,OAAA;AAAA,YAZJ,OAAM;AAAA,YACN,MAAK;AAAA,YACL,QAAO;AAAA,YACP,SAAQ;AAAA,YACR,eAAY;AAAA,UAAA;YAEZA,mBAKE,QAAA;AAAA,cAJA,kBAAe;AAAA,cACf,mBAAgB;AAAA,cAChB,gBAAa;AAAA,cACb,GAAE;AAAA,YAAA;;UAEAE,gBAAA,sBACH,UAAA,KAAS,GAAA,CAAA;AAAA,QAAA,MAIO,QAAA,yBAArBJ,mBA8CWK,UAAA,EAAA,KAAA,KAAA;AAAA,UA5CD,aAAA,sBADRL,mBAuBM,OAAA;AAAA;YArBJ,OAAM;AAAA,YACL,mCAAoB,QAAA,MAAM,MAAA;AAAA,UAAA;YAG3BE,mBAgBM,OAAA,EAhBD,OAAM,+BAA2B;AAAA,cACpCA,mBAaM,OAAA;AAAA,gBAZJ,OAAM;AAAA,gBACN,MAAK;AAAA,gBACL,QAAO;AAAA,gBACP,SAAQ;AAAA,gBACR,eAAY;AAAA,cAAA;gBAEZA,mBAKE,QAAA;AAAA,kBAJA,kBAAe;AAAA,kBACf,mBAAgB;AAAA,kBAChB,gBAAa;AAAA,kBACb,GAAE;AAAA,gBAAA;;cAGNA,mBAAgF,QAAA,EAA1E,OAAM,iCAAA,GAAiC,8BAA4B;AAAA,YAAA;mCAG7EF,mBAoBM,OAAA;AAAA;YAlBJ,OAAM;AAAA,YACL,mCAAoB,QAAA,MAAM,MAAA;AAAA,UAAA;YAE3BE,mBAaM,OAAA;AAAA,cAZJ,OAAM;AAAA,cACN,MAAK;AAAA,cACL,QAAO;AAAA,cACP,SAAQ;AAAA,cACR,eAAY;AAAA,YAAA;cAEZA,mBAKE,QAAA;AAAA,gBAJA,kBAAe;AAAA,gBACf,mBAAgB;AAAA,gBAChB,gBAAa;AAAA,gBACb,GAAE;AAAA,cAAA;;YAGNA,mBAAgE,QAAA,EAA1D,OAAM,iCAAA,GAAiC,gBAAY,EAAA;AAAA,UAAA;gCAK7DF,mBAuBWK,UAAA,EAAA,KAAA,KAAA;AAAA,UAtBTH,mBAME,OAAA;AAAA,qBALI;AAAA,YAAJ,KAAI;AAAA,YACJ,OAAM;AAAA,YACL,mCAAoB,QAAA,MAAM,MAAA;AAAA,YAC3B,MAAK;AAAA,YACL,cAAW;AAAA,UAAA;UAIbA,mBAYM,OAZN,YAYM;AAAA,YAXJA,mBAUS,UAAA;AAAA,cATP,MAAK;AAAA,cACL,OAAM;AAAA,cACL,UAAQ,CAAG,aAAA,SAAgB,QAAA;AAAA,cAC5B,cAAW;AAAA,cACV,SAAO;AAAA,YAAA;cAERA,mBAEM,OAAA;AAAA,gBAFD,MAAK;AAAA,gBAAO,QAAO;AAAA,gBAAe,SAAQ;AAAA,cAAA;gBAC7CA,mBAAyM,QAAA;AAAA,kBAAnM,kBAAe;AAAA,kBAAQ,mBAAgB;AAAA,kBAAQ,gBAAa;AAAA,kBAAI,GAAE;AAAA,gBAAA;;;;;QAQxE,QAAA,cAAc,aAAA,SAAY,CAAK,UAAA,SADvCC,UAAA,GAAAH,mBAMM,OANN,YAMM;AAAA,UAFJ,OAAA,CAAA,MAAA,OAAA,CAAA,IAAAE,mBAA6D,QAAA,EAAvD,OAAM,mCAAA,GAAmC,WAAO,EAAA;AAAA,0BAAO,MAC7DI,iBAAG,aAAA,eAAA,mBAAY,MAAM,GAAA,CAAA;AAAA,QAAA;;;;;"}
|
|
1
|
+
{"version":3,"file":"MoleculeInput.vue.js","sources":["../../src/components/MoleculeInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'\nimport type { MoleculeData } from '../types'\n\ninterface Props {\n modelValue?: MoleculeData\n disabled?: boolean\n readonly?: boolean\n height?: number\n showSmiles?: boolean\n placeholder?: string\n error?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n readonly: false,\n height: 300,\n showSmiles: true,\n placeholder: 'Draw a chemical structure',\n error: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [data: MoleculeData | undefined]\n 'error': [message: string]\n}>()\n\n// State\nconst containerRef = ref<HTMLDivElement | null>(null)\nconst jsmeInstance = ref<unknown>(null)\nconst isLoading = ref(true)\nconst loadError = ref<string | null>(null)\nconst debounceTimer = ref<ReturnType<typeof setTimeout> | null>(null)\n\n// Computed\nconst hasStructure = computed(() => {\n return props.modelValue?.smiles && props.modelValue.smiles.length > 0\n})\n\n// Use window-level state to persist across component instances and hot-reloads\ninterface JSMEGlobalState {\n __jsmeCallbacks__?: Array<() => void>\n __jsmeLoading__?: boolean\n __jsmeLoaded__?: boolean\n __jsmeLoadPromise__?: Promise<void>\n JSApplet?: { JSME?: new (id: string, width: string, height: string, options?: object) => unknown }\n jsmeOnLoad?: () => void\n}\n\nfunction getJSMEState(): JSMEGlobalState {\n const win = window as unknown as JSMEGlobalState\n if (!win.__jsmeCallbacks__) {\n win.__jsmeCallbacks__ = []\n }\n return win\n}\n\nfunction waitForJSME(): Promise<void> {\n const win = getJSMEState()\n\n // Already loaded\n if (win.JSApplet?.JSME) {\n return Promise.resolve()\n }\n\n // Reuse existing promise if loading\n if (win.__jsmeLoadPromise__) {\n return win.__jsmeLoadPromise__\n }\n\n win.__jsmeLoadPromise__ = new Promise((resolve, reject) => {\n // Double-check after promise creation\n if (win.JSApplet?.JSME) {\n resolve()\n return\n }\n\n // Set up global callback FIRST (before checking for existing script)\n const originalOnLoad = win.jsmeOnLoad\n win.jsmeOnLoad = () => {\n win.__jsmeLoaded__ = true\n originalOnLoad?.()\n win.__jsmeCallbacks__?.forEach(cb => cb())\n win.__jsmeCallbacks__ = []\n resolve()\n }\n\n // Add to callback queue\n win.__jsmeCallbacks__?.push(resolve)\n\n // Check if script already exists\n const existingScript = document.querySelector('script[data-jsme]')\n if (existingScript) {\n // Script exists, poll for JSApplet.JSME (in case jsmeOnLoad already fired)\n const checkReady = setInterval(() => {\n if (win.JSApplet?.JSME) {\n clearInterval(checkReady)\n win.__jsmeLoaded__ = true\n win.__jsmeCallbacks__?.forEach(cb => cb())\n win.__jsmeCallbacks__ = []\n resolve()\n }\n }, 100)\n\n setTimeout(() => {\n clearInterval(checkReady)\n if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {\n reject(new Error('JSME initialization timeout'))\n }\n }, 15000)\n return\n }\n\n // Load the script\n win.__jsmeLoading__ = true\n const script = document.createElement('script')\n script.src = 'https://jsme-editor.github.io/dist/jsme/jsme.nocache.js'\n script.integrity = 'sha384-l6tNzsc/eAJ7uql0dGAcHYI5ANVEV7DrJYjzXp3t13L+3OzLnfpzJO0Uio7mUSjY'\n script.crossOrigin = 'anonymous'\n script.async = true\n script.setAttribute('data-jsme', 'true')\n\n script.onerror = () => {\n win.__jsmeLoading__ = false\n win.__jsmeLoadPromise__ = undefined\n reject(new Error('Failed to load JSME script'))\n }\n\n // Timeout\n setTimeout(() => {\n if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {\n reject(new Error('JSME initialization timeout'))\n }\n }, 15000)\n\n document.head.appendChild(script)\n })\n\n return win.__jsmeLoadPromise__\n}\n\n// JSME initialization\nasync function initJSME() {\n if (!containerRef.value || props.readonly) return\n\n try {\n isLoading.value = true\n loadError.value = null\n\n // Wait for JSME to be ready\n await waitForJSME()\n\n // Wait for DOM to be ready\n await nextTick()\n\n if (!containerRef.value) return\n\n // Get JSME constructor from window\n const win = getJSMEState()\n\n if (!win.JSApplet?.JSME) {\n throw new Error('JSME library not available after loading')\n }\n\n // Create JSME instance\n const editorId = `jsme-${Date.now()}`\n containerRef.value.id = editorId\n\n const instance = new win.JSApplet.JSME(\n editorId,\n '100%',\n `${props.height}px`,\n {\n options: 'query,hydrogens,paste,depict',\n }\n )\n\n jsmeInstance.value = instance\n\n // Set initial value if provided\n if (props.modelValue?.molfile) {\n (instance as { readMolFile: (mol: string) => void }).readMolFile(props.modelValue.molfile)\n }\n\n // Set up change callback\n (instance as { setCallBack: (event: string, callback: () => void) => void }).setCallBack('AfterStructureModified', handleStructureChange)\n\n isLoading.value = false\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to load molecule editor'\n loadError.value = message\n emit('error', message)\n isLoading.value = false\n }\n}\n\nfunction handleStructureChange() {\n // Debounce the change event\n if (debounceTimer.value) {\n clearTimeout(debounceTimer.value)\n }\n\n debounceTimer.value = setTimeout(() => {\n if (!jsmeInstance.value) return\n\n const instance = jsmeInstance.value as {\n smiles: () => string\n molFile: () => string\n }\n\n const smiles = instance.smiles()\n const molfile = instance.molFile()\n\n if (!smiles || smiles.length === 0) {\n emit('update:modelValue', undefined)\n } else {\n emit('update:modelValue', { smiles, molfile })\n }\n }, 300)\n}\n\nfunction clearStructure() {\n if (jsmeInstance.value) {\n (jsmeInstance.value as { reset: () => void }).reset()\n }\n emit('update:modelValue', undefined)\n}\n\n// Watch for external value changes\nwatch(() => props.modelValue, (newValue) => {\n if (!jsmeInstance.value) return\n\n const instance = jsmeInstance.value as {\n smiles: () => string\n readMolFile: (mol: string) => void\n reset: () => void\n }\n\n // Only update if the external value differs from current\n const currentSmiles = instance.smiles()\n if (newValue?.smiles !== currentSmiles) {\n if (newValue?.molfile) {\n instance.readMolFile(newValue.molfile)\n } else {\n instance.reset()\n }\n }\n})\n\n// Lifecycle\nonMounted(() => {\n if (!props.readonly) {\n initJSME()\n } else {\n isLoading.value = false\n }\n})\n\nonUnmounted(() => {\n if (debounceTimer.value) {\n clearTimeout(debounceTimer.value)\n }\n jsmeInstance.value = null\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-molecule-input',\n disabled ? 'mld-molecule-input--disabled' : '',\n readonly ? 'mld-molecule-input--readonly' : '',\n error ? 'mld-molecule-input--error' : '',\n ]\"\n >\n <!-- Loading state -->\n <div\n v-if=\"isLoading && !readonly\"\n class=\"mld-molecule-input__skeleton\"\n :style=\"{ height: `${height}px` }\"\n >\n <svg\n class=\"mld-molecule-input__skeleton-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n <span class=\"mld-molecule-input__skeleton-text\">Loading molecule editor...</span>\n </div>\n\n <!-- Error state -->\n <div\n v-else-if=\"loadError\"\n class=\"mld-molecule-input__error\"\n >\n <svg\n class=\"mld-molecule-input__error-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n {{ loadError }}\n </div>\n\n <!-- Readonly mode - show empty or placeholder -->\n <template v-else-if=\"readonly\">\n <div\n v-if=\"hasStructure\"\n class=\"mld-molecule-input__readonly\"\n :style=\"{ height: `${height}px` }\"\n >\n <!-- In readonly mode, we just display a placeholder since we don't have SVG rendering -->\n <div class=\"mld-molecule-input__empty\">\n <svg\n class=\"mld-molecule-input__empty-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n <span class=\"mld-molecule-input__empty-text\">Structure defined (readonly)</span>\n </div>\n </div>\n <div\n v-else\n class=\"mld-molecule-input__empty\"\n :style=\"{ height: `${height}px` }\"\n >\n <svg\n class=\"mld-molecule-input__empty-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n <span class=\"mld-molecule-input__empty-text\">No structure</span>\n </div>\n </template>\n\n <!-- Editor mode -->\n <template v-else>\n <div\n ref=\"containerRef\"\n class=\"mld-molecule-input__editor\"\n :style=\"{ height: `${height}px` }\"\n role=\"application\"\n aria-label=\"Molecule structure editor\"\n />\n\n <!-- Actions toolbar -->\n <div class=\"mld-molecule-input__actions\">\n <button\n type=\"button\"\n class=\"mld-molecule-input__action-btn\"\n :disabled=\"!hasStructure || disabled\"\n aria-label=\"Clear structure\"\n @click=\"clearStructure\"\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 </div>\n </template>\n\n <!-- SMILES display -->\n <div\n v-if=\"showSmiles && hasStructure && !loadError\"\n class=\"mld-molecule-input__smiles\"\n >\n <span class=\"mld-molecule-input__smiles-label\">SMILES:</span>\n {{ modelValue?.smiles }}\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/molecule-input.css';\n</style>\n"],"names":["_a","_b","_createElementBlock","_normalizeClass","_createElementVNode","_openBlock","_createTextVNode","_Fragment","_toDisplayString"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAcA,UAAM,QAAQ;AASd,UAAM,OAAO;AAMb,UAAM,eAAe,IAA2B,IAAI;AACpD,UAAM,eAAe,IAAa,IAAI;AACtC,UAAM,YAAY,IAAI,IAAI;AAC1B,UAAM,YAAY,IAAmB,IAAI;AACzC,UAAM,gBAAgB,IAA0C,IAAI;AAGpE,UAAM,eAAe,SAAS,MAAM;;AAClC,eAAO,WAAM,eAAN,mBAAkB,WAAU,MAAM,WAAW,OAAO,SAAS;AAAA,IACtE,CAAC;AAYD,aAAS,eAAgC;AACvC,YAAM,MAAM;AACZ,UAAI,CAAC,IAAI,mBAAmB;AAC1B,YAAI,oBAAoB,CAAA;AAAA,MAC1B;AACA,aAAO;AAAA,IACT;AAEA,aAAS,cAA6B;;AACpC,YAAM,MAAM,aAAA;AAGZ,WAAI,SAAI,aAAJ,mBAAc,MAAM;AACtB,eAAO,QAAQ,QAAA;AAAA,MACjB;AAGA,UAAI,IAAI,qBAAqB;AAC3B,eAAO,IAAI;AAAA,MACb;AAEA,UAAI,sBAAsB,IAAI,QAAQ,CAAC,SAAS,WAAW;;AAEzD,aAAIA,MAAA,IAAI,aAAJ,gBAAAA,IAAc,MAAM;AACtB,kBAAA;AACA;AAAA,QACF;AAGA,cAAM,iBAAiB,IAAI;AAC3B,YAAI,aAAa,MAAM;;AACrB,cAAI,iBAAiB;AACrB;AACA,WAAAA,MAAA,IAAI,sBAAJ,gBAAAA,IAAuB,QAAQ,CAAA,OAAM,GAAA;AACrC,cAAI,oBAAoB,CAAA;AACxB,kBAAA;AAAA,QACF;AAGA,kBAAI,sBAAJ,mBAAuB,KAAK;AAG5B,cAAM,iBAAiB,SAAS,cAAc,mBAAmB;AACjE,YAAI,gBAAgB;AAElB,gBAAM,aAAa,YAAY,MAAM;;AACnC,iBAAIA,MAAA,IAAI,aAAJ,gBAAAA,IAAc,MAAM;AACtB,4BAAc,UAAU;AACxB,kBAAI,iBAAiB;AACrB,eAAAC,MAAA,IAAI,sBAAJ,gBAAAA,IAAuB,QAAQ,CAAA,OAAM,GAAA;AACrC,kBAAI,oBAAoB,CAAA;AACxB,sBAAA;AAAA,YACF;AAAA,UACF,GAAG,GAAG;AAEN,qBAAW,MAAM;;AACf,0BAAc,UAAU;AACxB,gBAAI,CAAC,IAAI,kBAAkB,GAACD,MAAA,IAAI,aAAJ,gBAAAA,IAAc,OAAM;AAC9C,qBAAO,IAAI,MAAM,6BAA6B,CAAC;AAAA,YACjD;AAAA,UACF,GAAG,IAAK;AACR;AAAA,QACF;AAGA,YAAI,kBAAkB;AACtB,cAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,eAAO,MAAM;AACb,eAAO,YAAY;AACnB,eAAO,cAAc;AACrB,eAAO,QAAQ;AACf,eAAO,aAAa,aAAa,MAAM;AAEvC,eAAO,UAAU,MAAM;AACrB,cAAI,kBAAkB;AACtB,cAAI,sBAAsB;AAC1B,iBAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,QAChD;AAGA,mBAAW,MAAM;;AACf,cAAI,CAAC,IAAI,kBAAkB,GAACA,MAAA,IAAI,aAAJ,gBAAAA,IAAc,OAAM;AAC9C,mBAAO,IAAI,MAAM,6BAA6B,CAAC;AAAA,UACjD;AAAA,QACF,GAAG,IAAK;AAER,iBAAS,KAAK,YAAY,MAAM;AAAA,MAClC,CAAC;AAED,aAAO,IAAI;AAAA,IACb;AAGA,mBAAe,WAAW;;AACxB,UAAI,CAAC,aAAa,SAAS,MAAM,SAAU;AAE3C,UAAI;AACF,kBAAU,QAAQ;AAClB,kBAAU,QAAQ;AAGlB,cAAM,YAAA;AAGN,cAAM,SAAA;AAEN,YAAI,CAAC,aAAa,MAAO;AAGzB,cAAM,MAAM,aAAA;AAEZ,YAAI,GAAC,SAAI,aAAJ,mBAAc,OAAM;AACvB,gBAAM,IAAI,MAAM,0CAA0C;AAAA,QAC5D;AAGA,cAAM,WAAW,QAAQ,KAAK,IAAA,CAAK;AACnC,qBAAa,MAAM,KAAK;AAExB,cAAM,WAAW,IAAI,IAAI,SAAS;AAAA,UAChC;AAAA,UACA;AAAA,UACA,GAAG,MAAM,MAAM;AAAA,UACf;AAAA,YACE,SAAS;AAAA,UAAA;AAAA,QACX;AAGF,qBAAa,QAAQ;AAGrB,aAAI,WAAM,eAAN,mBAAkB,SAAS;AAC5B,mBAAoD,YAAY,MAAM,WAAW,OAAO;AAAA,QAC3F;AAGC,iBAA4E,YAAY,0BAA0B,qBAAqB;AAExI,kBAAU,QAAQ;AAAA,MACpB,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,kBAAU,QAAQ;AAClB,aAAK,SAAS,OAAO;AACrB,kBAAU,QAAQ;AAAA,MACpB;AAAA,IACF;AAEA,aAAS,wBAAwB;AAE/B,UAAI,cAAc,OAAO;AACvB,qBAAa,cAAc,KAAK;AAAA,MAClC;AAEA,oBAAc,QAAQ,WAAW,MAAM;AACrC,YAAI,CAAC,aAAa,MAAO;AAEzB,cAAM,WAAW,aAAa;AAK9B,cAAM,SAAS,SAAS,OAAA;AACxB,cAAM,UAAU,SAAS,QAAA;AAEzB,YAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,eAAK,qBAAqB,MAAS;AAAA,QACrC,OAAO;AACL,eAAK,qBAAqB,EAAE,QAAQ,QAAA,CAAS;AAAA,QAC/C;AAAA,MACF,GAAG,GAAG;AAAA,IACR;AAEA,aAAS,iBAAiB;AACxB,UAAI,aAAa,OAAO;AACrB,qBAAa,MAAgC,MAAA;AAAA,MAChD;AACA,WAAK,qBAAqB,MAAS;AAAA,IACrC;AAGA,UAAM,MAAM,MAAM,YAAY,CAAC,aAAa;AAC1C,UAAI,CAAC,aAAa,MAAO;AAEzB,YAAM,WAAW,aAAa;AAO9B,YAAM,gBAAgB,SAAS,OAAA;AAC/B,WAAI,qCAAU,YAAW,eAAe;AACtC,YAAI,qCAAU,SAAS;AACrB,mBAAS,YAAY,SAAS,OAAO;AAAA,QACvC,OAAO;AACL,mBAAS,MAAA;AAAA,QACX;AAAA,MACF;AAAA,IACF,CAAC;AAGD,cAAU,MAAM;AACd,UAAI,CAAC,MAAM,UAAU;AACnB,iBAAA;AAAA,MACF,OAAO;AACL,kBAAU,QAAQ;AAAA,MACpB;AAAA,IACF,CAAC;AAED,gBAAY,MAAM;AAChB,UAAI,cAAc,OAAO;AACvB,qBAAa,cAAc,KAAK;AAAA,MAClC;AACA,mBAAa,QAAQ;AAAA,IACvB,CAAC;;;0BAICE,mBAwIM,OAAA;AAAA,QAvIH,OAAKC,eAAA;AAAA;UAAsC,QAAA,WAAQ,iCAAA;AAAA,UAA8C,QAAA,WAAQ,iCAAA;AAAA,UAA8C,QAAA,QAAK,8BAAA;AAAA,QAAA;;QASrJ,UAAA,UAAc,QAAA,yBADtBD,mBAoBM,OAAA;AAAA;UAlBJ,OAAM;AAAA,UACL,mCAAoB,QAAA,MAAM,MAAA;AAAA,QAAA;UAE3BE,mBAaM,OAAA;AAAA,YAZJ,OAAM;AAAA,YACN,MAAK;AAAA,YACL,QAAO;AAAA,YACP,SAAQ;AAAA,YACR,eAAY;AAAA,UAAA;YAEZA,mBAKE,QAAA;AAAA,cAJA,kBAAe;AAAA,cACf,mBAAgB;AAAA,cAChB,gBAAa;AAAA,cACb,GAAE;AAAA,YAAA;;UAGNA,mBAAiF,QAAA,EAA3E,OAAM,oCAAA,GAAoC,8BAA0B,EAAA;AAAA,QAAA,WAK/D,UAAA,SADbC,aAAAH,mBAmBM,OAnBN,YAmBM;AAAA,oCAfJE,mBAaM,OAAA;AAAA,YAZJ,OAAM;AAAA,YACN,MAAK;AAAA,YACL,QAAO;AAAA,YACP,SAAQ;AAAA,YACR,eAAY;AAAA,UAAA;YAEZA,mBAKE,QAAA;AAAA,cAJA,kBAAe;AAAA,cACf,mBAAgB;AAAA,cAChB,gBAAa;AAAA,cACb,GAAE;AAAA,YAAA;;UAEAE,gBAAA,sBACH,UAAA,KAAS,GAAA,CAAA;AAAA,QAAA,MAIO,QAAA,yBAArBJ,mBA8CWK,UAAA,EAAA,KAAA,KAAA;AAAA,UA5CD,aAAA,sBADRL,mBAuBM,OAAA;AAAA;YArBJ,OAAM;AAAA,YACL,mCAAoB,QAAA,MAAM,MAAA;AAAA,UAAA;YAG3BE,mBAgBM,OAAA,EAhBD,OAAM,+BAA2B;AAAA,cACpCA,mBAaM,OAAA;AAAA,gBAZJ,OAAM;AAAA,gBACN,MAAK;AAAA,gBACL,QAAO;AAAA,gBACP,SAAQ;AAAA,gBACR,eAAY;AAAA,cAAA;gBAEZA,mBAKE,QAAA;AAAA,kBAJA,kBAAe;AAAA,kBACf,mBAAgB;AAAA,kBAChB,gBAAa;AAAA,kBACb,GAAE;AAAA,gBAAA;;cAGNA,mBAAgF,QAAA,EAA1E,OAAM,iCAAA,GAAiC,8BAA4B;AAAA,YAAA;mCAG7EF,mBAoBM,OAAA;AAAA;YAlBJ,OAAM;AAAA,YACL,mCAAoB,QAAA,MAAM,MAAA;AAAA,UAAA;YAE3BE,mBAaM,OAAA;AAAA,cAZJ,OAAM;AAAA,cACN,MAAK;AAAA,cACL,QAAO;AAAA,cACP,SAAQ;AAAA,cACR,eAAY;AAAA,YAAA;cAEZA,mBAKE,QAAA;AAAA,gBAJA,kBAAe;AAAA,gBACf,mBAAgB;AAAA,gBAChB,gBAAa;AAAA,gBACb,GAAE;AAAA,cAAA;;YAGNA,mBAAgE,QAAA,EAA1D,OAAM,iCAAA,GAAiC,gBAAY,EAAA;AAAA,UAAA;gCAK7DF,mBAuBWK,UAAA,EAAA,KAAA,KAAA;AAAA,UAtBTH,mBAME,OAAA;AAAA,qBALI;AAAA,YAAJ,KAAI;AAAA,YACJ,OAAM;AAAA,YACL,mCAAoB,QAAA,MAAM,MAAA;AAAA,YAC3B,MAAK;AAAA,YACL,cAAW;AAAA,UAAA;UAIbA,mBAYM,OAZN,YAYM;AAAA,YAXJA,mBAUS,UAAA;AAAA,cATP,MAAK;AAAA,cACL,OAAM;AAAA,cACL,UAAQ,CAAG,aAAA,SAAgB,QAAA;AAAA,cAC5B,cAAW;AAAA,cACV,SAAO;AAAA,YAAA;cAERA,mBAEM,OAAA;AAAA,gBAFD,MAAK;AAAA,gBAAO,QAAO;AAAA,gBAAe,SAAQ;AAAA,cAAA;gBAC7CA,mBAAyM,QAAA;AAAA,kBAAnM,kBAAe;AAAA,kBAAQ,mBAAgB;AAAA,kBAAQ,gBAAa;AAAA,kBAAI,GAAE;AAAA,gBAAA;;;;;QAQxE,QAAA,cAAc,aAAA,SAAY,CAAK,UAAA,SADvCC,UAAA,GAAAH,mBAMM,OANN,YAMM;AAAA,UAFJ,OAAA,CAAA,MAAA,OAAA,CAAA,IAAAE,mBAA6D,QAAA,EAAvD,OAAM,mCAAA,GAAmC,WAAO,EAAA;AAAA,0BAAO,MAC7DI,iBAAG,aAAA,eAAA,mBAAY,MAAM,GAAA,CAAA;AAAA,QAAA;;;;;"}
|
|
@@ -57,6 +57,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
57
57
|
return openBlock(), createElementBlock("div", {
|
|
58
58
|
class: normalizeClass([
|
|
59
59
|
"mld-number-input",
|
|
60
|
+
`mld-number-input--${__props.size}`,
|
|
60
61
|
__props.error ? "mld-number-input--error" : "",
|
|
61
62
|
__props.disabled ? "mld-number-input--disabled" : ""
|
|
62
63
|
])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberInput.vue.js","sources":["../../src/components/NumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\ninterface Props {\n modelValue?: number\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n step: 1,\n disabled: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | undefined]\n}>()\n\nconst canDecrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.min === undefined) return true\n return props.modelValue > props.min\n})\n\nconst canIncrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.max === undefined) return true\n return props.modelValue < props.max\n})\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? undefined : Number(target.value)\n if (value !== undefined && !isNaN(value)) {\n emit('update:modelValue', clamp(value))\n } else {\n emit('update:modelValue', undefined)\n }\n}\n\nfunction decrement() {\n if (props.disabled || !canDecrement.value) return\n const current = props.modelValue ?? (props.max ?? 0)\n emit('update:modelValue', clamp(current - props.step))\n}\n\nfunction increment() {\n if (props.disabled || !canIncrement.value) return\n const current = props.modelValue ?? (props.min ?? 0)\n emit('update:modelValue', clamp(current + props.step))\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-number-input',\n error ? 'mld-number-input--error' : '',\n disabled ? 'mld-number-input--disabled' : '',\n ]\"\n >\n <button\n type=\"button\"\n aria-label=\"Decrease value\"\n :disabled=\"disabled || !canDecrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--decrement',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"decrement\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" />\n </svg>\n </button>\n\n <input\n type=\"number\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :class=\"[\n 'mld-number-input__input',\n `mld-number-input__input--${size}`,\n disabled ? 'mld-number-input__input--disabled' : '',\n ]\"\n @input=\"handleInput\"\n />\n\n <button\n type=\"button\"\n aria-label=\"Increase value\"\n :disabled=\"disabled || !canIncrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--increment',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"increment\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/number-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode"],"mappings":";;;;;;;;;;;;;;;;;;AAcA,UAAM,QAAQ;AAOd,UAAM,OAAO;AAIb,UAAM,eAAe,SAAS,MAAM;AAClC,UAAI,MAAM,eAAe,OAAW,QAAO;AAC3C,UAAI,MAAM,QAAQ,OAAW,QAAO;AACpC,aAAO,MAAM,aAAa,MAAM;AAAA,IAClC,CAAC;AAED,UAAM,eAAe,SAAS,MAAM;AAClC,UAAI,MAAM,eAAe,OAAW,QAAO;AAC3C,UAAI,MAAM,QAAQ,OAAW,QAAO;AACpC,aAAO,MAAM,aAAa,MAAM;AAAA,IAClC,CAAC;AAED,aAAS,MAAM,OAAuB;AACpC,UAAI,SAAS;AACb,UAAI,MAAM,QAAQ,UAAa,SAAS,MAAM,cAAc,MAAM;AAClE,UAAI,MAAM,QAAQ,UAAa,SAAS,MAAM,cAAc,MAAM;AAClE,aAAO;AAAA,IACT;AAEA,aAAS,YAAY,OAAc;AACjC,YAAM,SAAS,MAAM;AACrB,YAAM,QAAQ,OAAO,UAAU,KAAK,SAAY,OAAO,OAAO,KAAK;AACnE,UAAI,UAAU,UAAa,CAAC,MAAM,KAAK,GAAG;AACxC,aAAK,qBAAqB,MAAM,KAAK,CAAC;AAAA,MACxC,OAAO;AACL,aAAK,qBAAqB,MAAS;AAAA,MACrC;AAAA,IACF;AAEA,aAAS,YAAY;AACnB,UAAI,MAAM,YAAY,CAAC,aAAa,MAAO;AAC3C,YAAM,UAAU,MAAM,eAAe,MAAM,OAAO;AAClD,WAAK,qBAAqB,MAAM,UAAU,MAAM,IAAI,CAAC;AAAA,IACvD;AAEA,aAAS,YAAY;AACnB,UAAI,MAAM,YAAY,CAAC,aAAa,MAAO;AAC3C,YAAM,UAAU,MAAM,eAAe,MAAM,OAAO;AAClD,WAAK,qBAAqB,MAAM,UAAU,MAAM,IAAI,CAAC;AAAA,IACvD;;0BAIEA,
|
|
1
|
+
{"version":3,"file":"NumberInput.vue.js","sources":["../../src/components/NumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\ninterface Props {\n modelValue?: number\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n step: 1,\n disabled: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | undefined]\n}>()\n\nconst canDecrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.min === undefined) return true\n return props.modelValue > props.min\n})\n\nconst canIncrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.max === undefined) return true\n return props.modelValue < props.max\n})\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? undefined : Number(target.value)\n if (value !== undefined && !isNaN(value)) {\n emit('update:modelValue', clamp(value))\n } else {\n emit('update:modelValue', undefined)\n }\n}\n\nfunction decrement() {\n if (props.disabled || !canDecrement.value) return\n const current = props.modelValue ?? (props.max ?? 0)\n emit('update:modelValue', clamp(current - props.step))\n}\n\nfunction increment() {\n if (props.disabled || !canIncrement.value) return\n const current = props.modelValue ?? (props.min ?? 0)\n emit('update:modelValue', clamp(current + props.step))\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-number-input',\n `mld-number-input--${size}`,\n error ? 'mld-number-input--error' : '',\n disabled ? 'mld-number-input--disabled' : '',\n ]\"\n >\n <button\n type=\"button\"\n aria-label=\"Decrease value\"\n :disabled=\"disabled || !canDecrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--decrement',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"decrement\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" />\n </svg>\n </button>\n\n <input\n type=\"number\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :class=\"[\n 'mld-number-input__input',\n `mld-number-input__input--${size}`,\n disabled ? 'mld-number-input__input--disabled' : '',\n ]\"\n @input=\"handleInput\"\n />\n\n <button\n type=\"button\"\n aria-label=\"Increase value\"\n :disabled=\"disabled || !canIncrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--increment',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"increment\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/number-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode"],"mappings":"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"}
|
|
@@ -11,10 +11,10 @@ const _hoisted_5 = {
|
|
|
11
11
|
class: "mld-rack-editor__toolbar"
|
|
12
12
|
};
|
|
13
13
|
const _hoisted_6 = { class: "mld-rack-editor__toolbar-group" };
|
|
14
|
-
const _hoisted_7 = {
|
|
14
|
+
const _hoisted_7 = { class: "mld-rack-editor__toolbar-section" };
|
|
15
15
|
const _hoisted_8 = { class: "mld-rack-editor__format-btns" };
|
|
16
16
|
const _hoisted_9 = ["onClick"];
|
|
17
|
-
const _hoisted_10 = {
|
|
17
|
+
const _hoisted_10 = { class: "mld-rack-editor__toolbar-section" };
|
|
18
18
|
const _hoisted_11 = { class: "mld-rack-editor__slot-btns" };
|
|
19
19
|
const _hoisted_12 = ["onClick"];
|
|
20
20
|
const _hoisted_13 = {
|