diff --git a/apps/desktop/src/components/structure/TableStructureEditor.vue b/apps/desktop/src/components/structure/TableStructureEditor.vue index 5de0068b7..a94f1964f 100644 --- a/apps/desktop/src/components/structure/TableStructureEditor.vue +++ b/apps/desktop/src/components/structure/TableStructureEditor.vue @@ -92,9 +92,28 @@ const warnings = ref([]); const foreignKeys = ref([]); const triggers = ref([]); +const colWidths = ref([28, 128, 144, 96, 64, 56, 112, 128, 128]); +const colResizing = ref<{ col: number; startX: number; startW: number } | null>(null); const indexColWidths = ref([132, 200, 64, 96, 132, 160, 132, 76]); const resizing = ref<{ col: number; startX: number; startW: number } | null>(null); +function onColResize(e: MouseEvent, col: number) { + e.preventDefault(); + colResizing.value = { col, startX: e.clientX, startW: colWidths.value[col] }; + const onMove = (ev: MouseEvent) => { + if (!colResizing.value) return; + const delta = ev.clientX - colResizing.value.startX; + colWidths.value[col] = Math.max(28, colResizing.value.startW + delta); + }; + const onUp = () => { + colResizing.value = null; + document.removeEventListener("mousemove", onMove); + document.removeEventListener("mouseup", onUp); + }; + document.addEventListener("mousemove", onMove); + document.addEventListener("mouseup", onUp); +} + function onIndexColResize(e: MouseEvent, col: number) { e.preventDefault(); resizing.value = { col, startX: e.clientX, startW: indexColWidths.value[col] }; @@ -128,6 +147,17 @@ const indexTypeOptions = computed(() => structureCapabilities.value.indexType ? (indexTypesByDb[databaseType.value ?? ""] ?? []) : [], ); +const colLabels = computed(() => [ + "#", + t("structureEditor.columnName"), + t("structureEditor.dataType"), + t("structureEditor.length"), + t("structureEditor.nullable"), + t("structureEditor.primaryKey"), + t("structureEditor.defaultValue"), + t("structureEditor.comment"), + t("structureEditor.actions"), +]); const indexColLabels = computed(() => [ t("structureEditor.indexName"), t("structureEditor.indexColumns"), @@ -508,33 +538,26 @@ watch( - +
- - - - - - - - - diff --git a/apps/desktop/src/components/ui/searchable-select/SearchableSelect.vue b/apps/desktop/src/components/ui/searchable-select/SearchableSelect.vue index fb760e444..0191b5c5b 100644 --- a/apps/desktop/src/components/ui/searchable-select/SearchableSelect.vue +++ b/apps/desktop/src/components/ui/searchable-select/SearchableSelect.vue @@ -39,6 +39,8 @@ const emit = defineEmits<{ const open = ref(false); const searchText = ref(""); const searchInput = ref>(); +const listContainer = ref(); +const highlightIndex = ref(-1); const selectedLabel = computed(() => { if (!props.modelValue) return props.placeholder; @@ -55,6 +57,7 @@ watch(open, async (value) => { emit("update:open", value); if (!value) { searchText.value = ""; + highlightIndex.value = -1; return; } await nextTick(); @@ -62,6 +65,19 @@ watch(open, async (value) => { input?.focus(); }); +watch(searchText, () => { + highlightIndex.value = 0; +}); + +watch(highlightIndex, async () => { + await nextTick(); + const container = listContainer.value; + if (!container || highlightIndex.value < 0) return; + const buttons = container.querySelectorAll("button"); + const target = buttons[highlightIndex.value]; + target?.scrollIntoView({ block: "nearest" }); +}); + function selectOption(option: string) { emit("update:modelValue", option); open.value = false; @@ -71,6 +87,34 @@ function selectCustomOption() { if (!canSelectCustom.value) return; selectOption(customOptionValue.value); } + +function optionCount() { + return filteredOptions.value.length + (canSelectCustom.value ? 1 : 0); +} + +function handleKeydown(event: KeyboardEvent) { + if (event.key === "ArrowDown") { + event.preventDefault(); + const total = optionCount(); + if (total === 0) return; + highlightIndex.value = highlightIndex.value < total - 1 ? highlightIndex.value + 1 : 0; + } else if (event.key === "ArrowUp") { + event.preventDefault(); + const total = optionCount(); + if (total === 0) return; + highlightIndex.value = highlightIndex.value > 0 ? highlightIndex.value - 1 : total - 1; + } else if (event.key === "Enter") { + if (highlightIndex.value < 0 || highlightIndex.value >= optionCount()) return; + event.preventDefault(); + if (highlightIndex.value < filteredOptions.value.length) { + selectOption(filteredOptions.value[highlightIndex.value]); + } else { + selectCustomOption(); + } + } else if (event.key === "Escape") { + open.value = false; + } +}
# - {{ t("structureEditor.columnName") }} - - {{ t("structureEditor.dataType") }} - - {{ t("structureEditor.length") }} - - {{ t("structureEditor.nullable") }} - - {{ t("structureEditor.primaryKey") }} - - {{ t("structureEditor.defaultValue") }} - - {{ t("structureEditor.comment") }} - - {{ t("structureEditor.actions") }} + + {{ label }} +