fix: draw selected cell left border for first visible column in canvas mode

This commit is contained in:
t8y2 2026-06-11 19:35:08 +08:00
parent 7923f4915e
commit bd3585f057
3 changed files with 65 additions and 9 deletions

View File

@ -6232,11 +6232,17 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
</Transition>
<div v-if="isErrorResult" class="flex-1 flex flex-col items-center justify-center gap-2 px-6 text-center text-destructive">
<TriangleAlert class="h-8 w-8 text-destructive/50" aria-hidden="true" />
<div class="space-y-1">
<div class="space-y-1 select-text" @mousedown.stop @click.stop>
<div class="text-sm font-medium">{{ t("grid.queryError") }}</div>
<div class="text-xs max-w-lg break-all text-destructive/80">{{ errorMessage }}</div>
<div class="text-xs max-w-lg break-all cursor-text text-destructive/80 select-text">{{ errorMessage }}</div>
</div>
<div class="flex flex-wrap items-center justify-center gap-2">
<Button variant="outline" size="sm" class="h-7 gap-1.5 px-2 text-xs" @click.stop="copyText(errorMessage)">
<Copy class="h-3.5 w-3.5" />
{{ t("grid.copy") }}
</Button>
<slot name="error-actions" :error-message="errorMessage" />
</div>
<slot name="error-actions" :error-message="errorMessage" />
</div>
<div v-else-if="isTransposeMode" class="flex-1 flex flex-col min-h-0 overflow-hidden">
<div class="h-8 flex items-center gap-2 px-3 border-y shrink-0 bg-muted/20">

View File

@ -395,7 +395,7 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
const selectedLeftX = clippedX + 0.5;
const selectedRightX = clippedX + cellPaintWidth - 1.5;
const selectedTopY = Math.max(y + 0.5, 1);
const drawSelectedLeftBorder = selectedLeftX > rowNumberWidth + 0.5;
const drawSelectedLeftBorder = selectedLeftX >= rowNumberWidth + 0.5;
ctx.strokeStyle = theme.cellSelectedBorder;
ctx.beginPath();
ctx.moveTo(selectedLeftX, selectedTopY);

View File

@ -304,9 +304,33 @@ struct SearchHits {
hits: Vec<SearchHit>,
}
#[derive(Deserialize)]
struct HitsTotal {
value: u64,
enum HitsTotal {
Count(u64),
Value { value: u64 },
}
impl HitsTotal {
fn value(&self) -> u64 {
match self {
Self::Count(value) | Self::Value { value } => *value,
}
}
}
impl<'de> Deserialize<'de> for HitsTotal {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = serde_json::Value::deserialize(deserializer)?;
if let Some(count) = value.as_u64() {
return Ok(Self::Count(count));
}
if let Some(count) = value.get("value").and_then(serde_json::Value::as_u64) {
return Ok(Self::Value { value: count });
}
Err(serde::de::Error::custom("expected hits.total as a number or an object with value"))
}
}
#[derive(Deserialize)]
@ -351,7 +375,7 @@ pub async fn find_documents(
})
.collect();
Ok(MongoDocumentResult { documents, total: result.hits.total.value })
Ok(MongoDocumentResult { documents, total: result.hits.total.value() })
}
fn build_find_documents_body(
@ -1355,7 +1379,7 @@ fn parse_aggregations(aggs: &serde_json::Map<String, serde_json::Value>) -> (Vec
mod tests {
use super::{
build_find_documents_body, elasticsearch_accept_invalid_certs, elasticsearch_base_url_fallbacks,
redact_elasticsearch_url, EsClient,
redact_elasticsearch_url, EsClient, SearchResponse,
};
use serde_json::json;
use std::time::Duration;
@ -1490,4 +1514,30 @@ mod tests {
})
);
}
#[test]
fn parses_search_total_from_elasticsearch_6_number_shape() {
let response: SearchResponse = serde_json::from_value(json!({
"hits": {
"total": 5,
"hits": []
}
}))
.unwrap();
assert_eq!(response.hits.total.value(), 5);
}
#[test]
fn parses_search_total_from_elasticsearch_7_object_shape() {
let response: SearchResponse = serde_json::from_value(json!({
"hits": {
"total": { "value": 5, "relation": "eq" },
"hits": []
}
}))
.unwrap();
assert_eq!(response.hits.total.value(), 5);
}
}