mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-31 21:23:49 +08:00
Fix search and sort
This commit is contained in:
@@ -825,11 +825,11 @@ export function RouteRecordings() {
|
||||
</div>
|
||||
<label class="dashcam-sort">
|
||||
<span>Sort</span>
|
||||
<select @change="${event => { state.sortOrder = event.target.value }}">
|
||||
<option value="newest" selected="${() => state.sortOrder === "newest" || false}">Newest first</option>
|
||||
<option value="oldest" selected="${() => state.sortOrder === "oldest" || false}">Oldest first</option>
|
||||
<option value="longest" selected="${() => state.sortOrder === "longest" || false}">Longest duration</option>
|
||||
<option value="shortest" selected="${() => state.sortOrder === "shortest" || false}">Shortest duration</option>
|
||||
<select value="${() => state.sortOrder}" @change="${event => { state.sortOrder = event.target.value }}">
|
||||
<option value="newest">Newest first</option>
|
||||
<option value="oldest">Oldest first</option>
|
||||
<option value="longest">Longest duration</option>
|
||||
<option value="shortest">Shortest duration</option>
|
||||
</select>
|
||||
</label>
|
||||
<div class="dashcam-view-toggle" aria-label="Layout view mode">
|
||||
@@ -862,7 +862,7 @@ export function RouteRecordings() {
|
||||
<h2>${group.label}</h2>
|
||||
<span class="dashcam-date-group-count">${group.routes.length} ${group.routes.length === 1 ? "drive" : "drives"}</span>
|
||||
</div>
|
||||
${() => state.viewMode === "grid" ? html`<div class="screen-recordings-grid dashcam-routes-grid">
|
||||
${state.viewMode === "grid" ? html`<div class="screen-recordings-grid dashcam-routes-grid">
|
||||
${group.routes.map(route => html`
|
||||
<article class="${() => `recording-card dashcam-route-card ${route.is_preserved ? "is-preserved" : ""}`}" @click="${() => { state.selectedRoute = route }}">
|
||||
<div class="dashcam-card-top-bar">
|
||||
@@ -901,7 +901,7 @@ export function RouteRecordings() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>`)}
|
||||
</article>`.key(route.name))}
|
||||
</div>` : html`<div class="dashcam-routes-list">
|
||||
${group.routes.map(route => html`
|
||||
<article class="${() => `dashcam-route-row ${route.is_preserved ? "is-preserved" : ""}`}" @click="${() => { state.selectedRoute = route }}">
|
||||
@@ -940,9 +940,9 @@ export function RouteRecordings() {
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</article>`)}
|
||||
</article>`.key(route.name))}
|
||||
</div>`}
|
||||
</section>`)}
|
||||
</section>`.key(group.key))}
|
||||
</div>
|
||||
${view.truncated ? html`<p class="screen-recordings-message">Showing the first ${MAX_RENDERED_ROUTES} of ${view.matching.length} matching routes.</p>` : ""}`
|
||||
}}
|
||||
|
||||
@@ -40,6 +40,11 @@ export function normalizeRoute(route, locale) {
|
||||
`${routeDate.getMonth() + 1}/${routeDate.getDate()}/${routeDate.getFullYear()}`,
|
||||
`${routeDate.getFullYear()}-${routeDate.getMonth() + 1}-${routeDate.getDate()}`,
|
||||
] : []
|
||||
const timeAliases = routeDate ? [
|
||||
new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "2-digit" }).format(routeDate),
|
||||
new Intl.DateTimeFormat("en-US", { hour: "numeric", minute: "2-digit", hour12: true }).format(routeDate),
|
||||
`${String(routeDate.getHours()).padStart(2, "0")}:${String(routeDate.getMinutes()).padStart(2, "0")}`,
|
||||
] : []
|
||||
|
||||
return {
|
||||
...route,
|
||||
@@ -54,6 +59,7 @@ export function normalizeRoute(route, locale) {
|
||||
normalizeRouteSearchText(route?.name),
|
||||
normalizeRouteSearchText(isCustomName ? displayName : ""),
|
||||
...dateAliases.map(normalizeRouteSearchText),
|
||||
...timeAliases.map(normalizeRouteSearchText),
|
||||
].filter(Boolean),
|
||||
}
|
||||
}
|
||||
@@ -120,7 +126,8 @@ export function sortRoutes(routes, sortOrder = "newest") {
|
||||
}
|
||||
|
||||
export function routeMatchesSearch(route, searchQuery) {
|
||||
const queryTokens = normalizeRouteSearchText(searchQuery).split(" ").filter(Boolean)
|
||||
const normalizedQuery = normalizeRouteSearchText(searchQuery)
|
||||
const queryTokens = normalizedQuery.split(" ").filter(Boolean)
|
||||
if (!queryTokens.length) return true
|
||||
|
||||
const searchValues = Array.isArray(route?._searchValues) ? route._searchValues : [
|
||||
@@ -130,8 +137,9 @@ export function routeMatchesSearch(route, searchQuery) {
|
||||
route?.displayDate,
|
||||
].map(normalizeRouteSearchText).filter(Boolean)
|
||||
return searchValues.some(value => {
|
||||
if (value.includes(normalizedQuery)) return true
|
||||
const searchTokens = value.split(" ").filter(Boolean)
|
||||
return queryTokens.every(queryToken => searchTokens.some(searchToken => searchToken.includes(queryToken)))
|
||||
return queryTokens.every(queryToken => searchTokens.some(searchToken => searchToken.startsWith(queryToken)))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,14 @@ def test_route_titles_and_custom_name_badges_are_reactive():
|
||||
assert source.count('${() => route.isCustomName ? html`') >= 4
|
||||
|
||||
|
||||
def test_sort_order_select_and_route_items_are_keyed_and_reactive():
|
||||
source = COMPONENT_PATH.read_text(encoding="utf-8")
|
||||
|
||||
assert '<select value="${() => state.sortOrder}" @change="${event => { state.sortOrder = event.target.value }}">' in source
|
||||
assert ".key(group.key)" in source
|
||||
assert ".key(route.name)" in source
|
||||
|
||||
|
||||
def test_player_shell_matches_low_quality_video_aspect_ratio():
|
||||
source = COMPONENT_CSS_PATH.read_text(encoding="utf-8")
|
||||
|
||||
@@ -163,6 +171,24 @@ def test_route_search_input_updates_on_every_keystroke():
|
||||
assert '@input="${event => { state.searchQuery = event.target.value }}"' in source
|
||||
|
||||
|
||||
def test_search_indexes_the_displayed_time_for_every_route():
|
||||
result = evaluate('''
|
||||
const routes = [
|
||||
route("0000006a--9f0a7bdf9c", "2026-08-27T12:15:00Z"),
|
||||
route("0000006b--9f0a7bdf9d", "2026-08-27T12:50:00Z"),
|
||||
route("0000006c--9f0a7bdf9e", "2026-08-27T13:05:00Z"),
|
||||
]
|
||||
return ["12", "12:5", "12:15"]
|
||||
.map(searchQuery => buildRouteView(routes, { searchQuery }).matching.map(item => item.name))
|
||||
''')
|
||||
|
||||
assert result == [
|
||||
["0000006b--9f0a7bdf9d", "0000006a--9f0a7bdf9c"],
|
||||
["0000006b--9f0a7bdf9d"],
|
||||
["0000006a--9f0a7bdf9c"],
|
||||
]
|
||||
|
||||
|
||||
def test_filters_preserved_routes_before_applying_the_render_limit():
|
||||
view = evaluate('''
|
||||
const routes = Array.from({ length: MAX_RENDERED_ROUTES + 25 }, (_, index) => route(
|
||||
|
||||
Reference in New Issue
Block a user