fix(html-examples): keep keyed search helper local
Inline keyed search reconciliation so the public examples contract does not see low-level advanced primitives while preserving remove/replace/append behavior. req: list/006 req: examples/001
This commit is contained in:
@@ -38,7 +38,7 @@ Status legend:
|
|||||||
| `lazy-load` | implemented | `data-hemx-revealed` dispatches a generated form once when visible; the server swaps a generated lazy panel. | `gallery.heml`, `gallery_handlers::lazy_load`, `LazyPanel` |
|
| `lazy-load` | implemented | `data-hemx-revealed` dispatches a generated form once when visible; the server swaps a generated lazy panel. | `gallery.heml`, `gallery_handlers::lazy_load`, `LazyPanel` |
|
||||||
| `inline-validation` | implemented | A generated form reports field failure with `validate_email_form.error(...)`, focuses the field, and updates status text. | `templates/gallery.heml`, `gallery_handlers::validate_email` |
|
| `inline-validation` | implemented | A generated form reports field failure with `validate_email_form.error(...)`, focuses the field, and updates status text. | `templates/gallery.heml`, `gallery_handlers::validate_email` |
|
||||||
| `infinite-scroll` | implemented | A revealed sentinel form posts to the same server-owned loading model and replaces generated keyed rows. | `gallery_handlers::infinite_scroll`, `data-hemx-revealed`, `infinite_row` |
|
| `infinite-scroll` | implemented | A revealed sentinel form posts to the same server-owned loading model and replaces generated keyed rows. | `gallery_handlers::infinite_scroll`, `data-hemx-revealed`, `infinite_row` |
|
||||||
| `active-search` | implemented | The search form posts a query; the server derives result rows and reconciles generated keyed partials by removing filtered-out keys, replacing retained keys, and appending newly visible keys. | `gallery_handlers::search`, `keyed_filter_effects`, `SearchResult` |
|
| `active-search` | implemented | The search form posts a query; the server derives result rows and reconciles generated keyed partials by removing filtered-out keys, replacing retained keys, and appending newly visible keys. | `gallery_handlers::search`, `SearchResult` |
|
||||||
| `progress-bar` | implemented | `data-hemx-interval` ticks a server-owned progress value and replaces a generated progress partial. | `gallery_handlers::tick_progress`, `ProgressMeter` |
|
| `progress-bar` | implemented | `data-hemx-interval` ticks a server-owned progress value and replaces a generated progress partial. | `gallery_handlers::tick_progress`, `ProgressMeter` |
|
||||||
| `value-select` | implemented | The first select posts a generated form; the server derives and replaces generated option rows for the second select. | `gallery_handlers::choose_category`, `ValueOption` |
|
| `value-select` | implemented | The first select posts a generated form; the server derives and replaces generated option rows for the second select. | `gallery_handlers::choose_category`, `ValueOption` |
|
||||||
| `animations` | integration-owned | CSS transitions are presentation policy around generated replacements; hemx should only preserve stable DOM boundaries. | Use keyed partials and app CSS; no core animation framework. |
|
| `animations` | integration-owned | CSS transitions are presentation policy around generated replacements; hemx should only preserve stable DOM boundaries. | Use keyed partials and app CSS; no core animation framework. |
|
||||||
|
|||||||
@@ -458,47 +458,6 @@ fn search_results_for(query: &str) -> Vec<SearchResult> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep filtered keyed collections stable: remove filtered-out rows, replace retained
|
|
||||||
// rows, and append newly visible rows instead of clearing the whole list. req: list/006
|
|
||||||
fn keyed_filter_effects<T, K>(
|
|
||||||
previous: impl IntoIterator<Item = T>,
|
|
||||||
current: impl IntoIterator<Item = T>,
|
|
||||||
key: impl Fn(&T) -> K,
|
|
||||||
append: impl Fn(T) -> hemx::advanced::Effect,
|
|
||||||
replace: impl Fn(T) -> hemx::advanced::Effect,
|
|
||||||
remove: impl Fn(String) -> hemx::advanced::Effect,
|
|
||||||
) -> Vec<hemx::advanced::Effect>
|
|
||||||
where
|
|
||||||
K: ToString,
|
|
||||||
{
|
|
||||||
let previous_keys = previous
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| key(&item).to_string())
|
|
||||||
.collect::<std::collections::BTreeSet<_>>();
|
|
||||||
let current = current
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| (key(&item).to_string(), item))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let current_keys = current
|
|
||||||
.iter()
|
|
||||||
.map(|(key, _)| key.clone())
|
|
||||||
.collect::<std::collections::BTreeSet<_>>();
|
|
||||||
|
|
||||||
let mut effects = previous_keys
|
|
||||||
.difference(¤t_keys)
|
|
||||||
.cloned()
|
|
||||||
.map(remove)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
effects.extend(current.into_iter().map(|(item_key, item)| {
|
|
||||||
if previous_keys.contains(&item_key) {
|
|
||||||
replace(item)
|
|
||||||
} else {
|
|
||||||
append(item)
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
effects
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hemx::component("gallery")]
|
#[hemx::component("gallery")]
|
||||||
mod gallery_handlers {
|
mod gallery_handlers {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -640,14 +599,30 @@ mod gallery_handlers {
|
|||||||
*stored_query = query.clone();
|
*stored_query = query.clone();
|
||||||
previous_query
|
previous_query
|
||||||
};
|
};
|
||||||
let mut effects = keyed_filter_effects(
|
let previous_results = search_results_for(&previous_query);
|
||||||
search_results_for(&previous_query),
|
let current_results = search_results_for(&query);
|
||||||
search_results_for(&query),
|
let previous_keys = previous_results
|
||||||
|result| result.id,
|
.iter()
|
||||||
|result| gallery::search_result.append(result),
|
.map(|result| result.id.to_string())
|
||||||
|result| gallery::search_result.replace(result),
|
.collect::<std::collections::BTreeSet<_>>();
|
||||||
|key| gallery::search_result.remove(key),
|
let current_keys = current_results
|
||||||
);
|
.iter()
|
||||||
|
.map(|result| result.id.to_string())
|
||||||
|
.collect::<std::collections::BTreeSet<_>>();
|
||||||
|
// Keep filtered keyed collections stable: remove filtered-out rows, replace retained
|
||||||
|
// rows, and append newly visible rows instead of clearing the whole list. req: list/006
|
||||||
|
let mut effects = previous_keys
|
||||||
|
.difference(¤t_keys)
|
||||||
|
.cloned()
|
||||||
|
.map(|key| gallery::search_result.remove(key))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
effects.extend(current_results.into_iter().map(|result| {
|
||||||
|
if previous_keys.contains(&result.id.to_string()) {
|
||||||
|
gallery::search_result.replace(result)
|
||||||
|
} else {
|
||||||
|
gallery::search_result.append(result)
|
||||||
|
}
|
||||||
|
}));
|
||||||
effects.push(gallery::search_status.set(if query.is_empty() {
|
effects.push(gallery::search_status.set(if query.is_empty() {
|
||||||
"Showing all results".into()
|
"Showing all results".into()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user