| 1166 |
1166 |
|
css
|
| 1167 |
1167 |
|
}
|
| 1168 |
1168 |
|
|
|
1169 |
+ |
/// A time axis: the container, its gridlines and ruler, and the placed things.
|
|
1170 |
+ |
///
|
|
1171 |
+ |
/// `makeover-layout` 0.24.0's [`Track`](makeover_layout::Track), and the first
|
|
1172 |
+ |
/// member here whose whole point is *position*. That makes the magnitude line
|
|
1173 |
+ |
/// this crate keeps worth restating rather than assuming.
|
|
1174 |
+ |
///
|
|
1175 |
+ |
/// # Where the numbers come from
|
|
1176 |
+ |
///
|
|
1177 |
+ |
/// Every value that varies per item is a custom property the caller sets
|
|
1178 |
+ |
/// inline, and every rule here reads one. `--track-at` and `--track-for` are
|
|
1179 |
+ |
/// percentages of the span, which
|
|
1180 |
+ |
/// [`Track::fraction`](makeover_layout::Track::fraction) computes once so three
|
|
1181 |
+ |
/// renderers cannot disagree about it. Nothing here knows a pixel.
|
|
1182 |
+ |
///
|
|
1183 |
+ |
/// That is what lets the stylesheet stay static while the items move: an entry
|
|
1184 |
+ |
/// carries `style="--track-at: 37.5%; --track-for: 4.166%"` and the rule below
|
|
1185 |
+ |
/// turns it into a box. The alternative was emitting a rule per item, which is
|
|
1186 |
+ |
/// a stylesheet that grows with the data.
|
|
1187 |
+ |
///
|
|
1188 |
+ |
/// **The height of the track is the app's**, not this crate's. 96 quarter-hour
|
|
1189 |
+ |
/// slots at some slot height is a size, and a size is `makeover-geometry`'s
|
|
1190 |
+ |
/// question -- the same refusal `figure_rules` and `placeholder_rules` make.
|
|
1191 |
+ |
/// Percentages need a resolved height above them, so `.track` gets
|
|
1192 |
+ |
/// `position: relative` and nothing else; the app says how tall a day is.
|
|
1193 |
+ |
///
|
|
1194 |
+ |
/// # Overlap
|
|
1195 |
+ |
///
|
|
1196 |
+ |
/// Two things at the same time is intrinsic to an axis and has no analogue in a
|
|
1197 |
+ |
/// list. The description does not declare it -- `Placement::overlaps` derives it
|
|
1198 |
+ |
/// from the times -- so what arrives here is a lane index and a lane count, and
|
|
1199 |
+ |
/// the rule divides the width. A renderer that would rather stack them ignores
|
|
1200 |
+ |
/// both properties and the defaults give it one full-width lane.
|
|
1201 |
+ |
fn track_rules(opts: &Emit) -> String {
|
|
1202 |
+ |
let track = class("track", opts);
|
|
1203 |
+ |
let slot = class("track-slot", opts);
|
|
1204 |
+ |
let tick = class("track-tick", opts);
|
|
1205 |
+ |
let entry = class("track-entry", opts);
|
|
1206 |
+ |
let mut css = String::new();
|
|
1207 |
+ |
|
|
1208 |
+ |
// The positioning context every entry resolves against, and the whole of
|
|
1209 |
+ |
// what this crate says about the container. No height: see the doc above.
|
|
1210 |
+ |
let _ = writeln!(css, ".{track} {{\n position: relative;\n}}");
|
|
1211 |
+ |
|
|
1212 |
+ |
// Gridlines and the ruler are the axis reading itself back, which is
|
|
1213 |
+ |
// `picture-caption` and `showing-position`'s claim: about the thing rather
|
|
1214 |
+ |
// than one of the things.
|
|
1215 |
+ |
//
|
|
1216 |
+ |
// `border_width` rather than a literal, the way `depth_rule` writes its
|
|
1217 |
+ |
// edge. A gridline is the one place a timeline would most naturally reach
|
|
1218 |
+ |
// for a hardcoded 1px, and a hardcoded 1px is this crate naming a size.
|
|
1219 |
+ |
let _ = writeln!(
|
|
1220 |
+ |
css,
|
|
1221 |
+ |
".{slot} {{\n border-top: {} solid var(--border);\n}}",
|
|
1222 |
+ |
opts.border_width
|
|
1223 |
+ |
);
|
|
1224 |
+ |
let _ = writeln!(css, ".{tick} {{\n color: var(--content-muted);\n}}");
|
|
1225 |
+ |
|
|
1226 |
+ |
// The one rule that does real work. Top and height are the placement;
|
|
1227 |
+ |
// left and width are the lane. Both lane properties default so an entry
|
|
1228 |
+ |
// that names neither is full width, which is the common case and the one a
|
|
1229 |
+ |
// renderer gets for free.
|
|
1230 |
+ |
let _ = writeln!(
|
|
1231 |
+ |
css,
|
|
1232 |
+ |
".{entry} {{\n \
|
|
1233 |
+ |
position: absolute;\n \
|
|
1234 |
+ |
top: var(--track-at, 0%);\n \
|
|
1235 |
+ |
height: var(--track-for, 100%);\n \
|
|
1236 |
+ |
left: calc(var(--track-lane, 0) / var(--track-lanes, 1) * 100%);\n \
|
|
1237 |
+ |
width: calc(100% / var(--track-lanes, 1));\n\
|
|
1238 |
+ |
}}"
|
|
1239 |
+ |
);
|
|
1240 |
+ |
|
|
1241 |
+ |
css
|
|
1242 |
+ |
}
|
|
1243 |
+ |
|
| 1169 |
1244 |
|
/// A region's stand-in, and the header of a table that can be reordered.
|
| 1170 |
1245 |
|
///
|
| 1171 |
1246 |
|
/// Both are 0.12.0 members and both are colour and affordance only, which is
|
| 1370 |
1445 |
|
css.push_str(&figure_rules(opts));
|
| 1371 |
1446 |
|
css.push_str(&picture_rules(opts));
|
| 1372 |
1447 |
|
css.push_str(&showing_rules(opts));
|
|
1448 |
+ |
css.push_str(&track_rules(opts));
|
| 1373 |
1449 |
|
css.push_str(&state_rules(opts));
|
| 1374 |
1450 |
|
css.push_str(&table_rules(opts));
|
| 1375 |
1451 |
|
css
|
| 2149 |
2225 |
|
assert!(!css.contains("nth-child"), "{css}");
|
| 2150 |
2226 |
|
}
|
| 2151 |
2227 |
|
|
|
2228 |
+ |
#[test]
|
|
2229 |
+ |
fn a_track_places_by_custom_property_and_never_by_a_size() {
|
|
2230 |
+ |
let css = track_rules(&Emit::default());
|
|
2231 |
+ |
|
|
2232 |
+ |
// Placement arrives from the caller, computed once by Track::fraction.
|
|
2233 |
+ |
// If either of these becomes a literal, three renderers have started
|
|
2234 |
+ |
// disagreeing about where 09:30 is.
|
|
2235 |
+ |
assert!(css.contains("top: var(--track-at"), "{css}");
|
|
2236 |
+ |
assert!(css.contains("height: var(--track-for"), "{css}");
|
|
2237 |
+ |
|
|
2238 |
+ |
// Overlap lanes default so an entry naming neither is full width.
|
|
2239 |
+ |
assert!(css.contains("--track-lane, 0"), "{css}");
|
|
2240 |
+ |
assert!(css.contains("--track-lanes, 1"), "{css}");
|
|
2241 |
+ |
|
|
2242 |
+ |
// The refusal that matters. A slot height here would be this crate
|
|
2243 |
+ |
// deciding how tall a quarter of an hour is, which is the thing
|
|
2244 |
+ |
// makeover-geometry owns and the reason `.track` gets no height at all.
|
|
2245 |
+ |
assert!(!css.contains("height: var(--track-slot"), "{css}");
|
|
2246 |
+ |
for size in ["px", "rem", "em", "vh"] {
|
|
2247 |
+ |
let bare = css
|
|
2248 |
+ |
.lines()
|
|
2249 |
+ |
.filter(|l| !l.contains("var(--"))
|
|
2250 |
+ |
.any(|l| l.contains(size));
|
|
2251 |
+ |
assert!(!bare, "track_rules named a {size} outside a var(): {css}");
|
|
2252 |
+ |
}
|
|
2253 |
+ |
}
|
|
2254 |
+ |
|
| 2152 |
2255 |
|
#[test]
|
| 2153 |
2256 |
|
fn the_whole_sheet_still_names_every_colour() {
|
| 2154 |
2257 |
|
// The crate's founding property, asserted over the component layer and
|
| 2228 |
2331 |
|
| "nowrap"
|
| 2229 |
2332 |
|
| "100%"
|
| 2230 |
2333 |
|
| "1%"
|
|
2334 |
+ |
// The time axis, 0.42.0. `position` is the one
|
|
2335 |
+ |
// property whose whole job is where a thing sits,
|
|
2336 |
+ |
// which is exactly what this crate spent its life
|
|
2337 |
+ |
// refusing to say -- so it is worth being exact
|
|
2338 |
+ |
// about why these two are not that refusal
|
|
2339 |
+ |
// breaking.
|
|
2340 |
+ |
//
|
|
2341 |
+ |
// Neither names a magnitude. `relative` says the
|
|
2342 |
+ |
// track is what its entries resolve against, and
|
|
2343 |
+ |
// `absolute` says an entry is placed rather than
|
|
2344 |
+ |
// flowed. *Where* each entry lands is
|
|
2345 |
+ |
// `--track-at` and `--track-for`, custom
|
|
2346 |
+ |
// properties the caller sets from
|
|
2347 |
+ |
// `Track::fraction`, and they are skipped by the
|
|
2348 |
+ |
// `var(--` arm above like every other value this
|
|
2349 |
+ |
// crate refuses to decide.
|
|
2350 |
+ |
//
|
|
2351 |
+ |
// The rule that would break the refusal is a slot
|
|
2352 |
+ |
// height, and there is none: the track's height is
|
|
2353 |
+ |
// the app's, so the percentages have something to
|
|
2354 |
+ |
// resolve against and this crate still never says
|
|
2355 |
+ |
// how tall a day is.
|
|
2356 |
+ |
| "relative"
|
|
2357 |
+ |
| "absolute"
|
| 2231 |
2358 |
|
// A picture's three, 0.36.0. `block` is structure
|
| 2232 |
2359 |
|
// for the reason `table` is: an inline image sits
|
| 2233 |
2360 |
|
// on the baseline and carries a descender's worth
|