| 122 |
122 |
|
/// Both halves together: the shortened track list, and `display: none` on each
|
| 123 |
123 |
|
/// dropped column *by its own class*. Nothing counts positions, so inserting a
|
| 124 |
124 |
|
/// column changes what is emitted rather than changing which column vanishes.
|
|
125 |
+ |
///
|
|
126 |
+ |
/// `selector` may be a selector list. A descendant is appended to each part
|
|
127 |
+ |
/// rather than to the whole, because appending to the whole changes what the
|
|
128 |
+ |
/// earlier parts match: `.head, .row > .col-x` reads as "`.head`, or a `.col-x`
|
|
129 |
+ |
/// inside `.row`", so `.head` itself would be hidden.
|
| 125 |
130 |
|
#[must_use]
|
| 126 |
131 |
|
pub fn narrowing_css(
|
| 127 |
132 |
|
columns: &[Column<'_>],
|
| 130 |
135 |
|
cutoff: Priority,
|
| 131 |
136 |
|
opts: &Emit,
|
| 132 |
137 |
|
) -> String {
|
|
138 |
+ |
let parts: Vec<&str> = selector.split(',').map(str::trim).collect();
|
|
139 |
+ |
|
| 133 |
140 |
|
let mut css = format!(
|
| 134 |
|
- |
"{selector} {{\n grid-template-columns: {};\n}}\n",
|
|
141 |
+ |
"{} {{\n grid-template-columns: {};\n}}\n",
|
|
142 |
+ |
parts.join(", "),
|
| 135 |
143 |
|
grid_template_columns(columns, sizing, cutoff)
|
| 136 |
144 |
|
);
|
| 137 |
145 |
|
|
| 138 |
146 |
|
for column in columns.iter().filter(|c| !c.kept_at(cutoff)) {
|
|
147 |
+ |
let class = column_class(column, opts);
|
|
148 |
+ |
let targets: Vec<String> = parts
|
|
149 |
+ |
.iter()
|
|
150 |
+ |
.map(|part| format!("{part} > .{class}"))
|
|
151 |
+ |
.collect();
|
| 139 |
152 |
|
let _ = write!(
|
| 140 |
153 |
|
css,
|
| 141 |
|
- |
"{selector} > .{} {{\n display: none;\n}}\n",
|
| 142 |
|
- |
column_class(column, opts)
|
|
154 |
+ |
"{} {{\n display: none;\n}}\n",
|
|
155 |
+ |
targets.join(",\n")
|
| 143 |
156 |
|
);
|
| 144 |
157 |
|
}
|
| 145 |
158 |
|
css
|
| 202 |
215 |
|
/// Emits the cells alone, not the row element. The row carries the app's
|
| 203 |
216 |
|
/// identity and hooks — `data-id`, a context-menu binding, a tabindex, its
|
| 204 |
217 |
|
/// state classes — and none of that is describable here.
|
|
218 |
+ |
///
|
|
219 |
+ |
/// # Not for a webview's scroll path
|
|
220 |
+ |
///
|
|
221 |
+ |
/// This has no consumer in either webview app, deliberately, and wiring one in
|
|
222 |
+ |
/// would be a mistake worth naming. goingson renders rows through a virtual
|
|
223 |
+ |
/// scroller whose `_render` calls its row builder **synchronously** while
|
|
224 |
+ |
/// scrolling; the code's own comment says scroll events fire at 60Hz+ and that
|
|
225 |
+ |
/// this is the hot path. Reaching Rust from there means an IPC round trip and
|
|
226 |
+ |
/// an `await` in that loop, per visible range, during a drag.
|
|
227 |
+ |
///
|
|
228 |
+ |
/// So this is for the hosts where rendering already happens in Rust: an axum
|
|
229 |
+ |
/// route, and the router when it lands. There the objection does not apply,
|
|
230 |
+ |
/// because nothing crosses a process boundary to reach it. A webview app should
|
|
231 |
+ |
/// take [`narrowing_css`] and [`column_class`] and keep building its own rows.
|
| 205 |
232 |
|
#[must_use]
|
| 206 |
233 |
|
pub fn cells_html(columns: &[Column<'_>], cells: &[Cell<'_>], opts: &Emit) -> String {
|
| 207 |
234 |
|
let cell_class = class("cell", opts);
|
| 310 |
337 |
|
assert!(!css.contains(".col-due {\n display: none"), "{css}");
|
| 311 |
338 |
|
}
|
| 312 |
339 |
|
|
|
340 |
+ |
/// A selector list has to distribute, or the earlier parts of it get the
|
|
341 |
+ |
/// child combinator appended to the whole and start matching things they
|
|
342 |
+ |
/// never named. This hid an entire table header the first time it ran.
|
|
343 |
+ |
#[test]
|
|
344 |
+ |
fn a_selector_list_distributes_the_hidden_column() {
|
|
345 |
+ |
let css = narrowing_css(
|
|
346 |
+ |
&columns(),
|
|
347 |
+ |
".task-header-row, .task-row",
|
|
348 |
+ |
&sizing(),
|
|
349 |
+ |
Priority::Secondary,
|
|
350 |
+ |
&Emit::default(),
|
|
351 |
+ |
);
|
|
352 |
+ |
assert!(css.contains(".task-header-row > .col-progress,\n.task-row > .col-progress {"), "{css}");
|
|
353 |
+ |
// The bare header selector must never appear as a hiding target.
|
|
354 |
+ |
assert!(!css.contains(".task-header-row {\n display: none"), "{css}");
|
|
355 |
+ |
assert!(css.contains(".task-header-row, .task-row {\n grid-template-columns:"), "{css}");
|
|
356 |
+ |
}
|
|
357 |
+ |
|
| 313 |
358 |
|
#[test]
|
| 314 |
359 |
|
fn cells_follow_the_columns_and_carry_their_column_class() {
|
| 315 |
360 |
|
let cells = [
|