| 415 |
415 |
|
mod corpus;
|
| 416 |
416 |
|
|
| 417 |
417 |
|
use crate::list::{cell_part_class, part_class};
|
| 418 |
|
- |
use crate::reset::Reset;
|
|
418 |
+ |
use crate::reset::{Chrome, Reset};
|
| 419 |
419 |
|
use makeover_geometry::{Density, SizeClass};
|
| 420 |
420 |
|
// Re-exported rather than redefined. An app assembling its own stylesheet out
|
| 421 |
421 |
|
// of this crate's pieces needs the same layer name, and most such apps depend
|
| 757 |
757 |
|
)
|
| 758 |
758 |
|
}
|
| 759 |
759 |
|
|
|
760 |
+ |
/// A rest depth said out loud on both axes, for a rule that has to beat the
|
|
761 |
+ |
/// states above it.
|
|
762 |
+ |
///
|
|
763 |
+ |
/// [`depth_declarations`] states an axis only when the depth has something to
|
|
764 |
+ |
/// say about it, which is right for a rest rule: a [`Depth::Flat`] region
|
|
765 |
+ |
/// inherits what it sits on, and asserting `background: none` there would be
|
|
766 |
+ |
/// the difference between level-with and painted-transparent. It is wrong for
|
|
767 |
+ |
/// a rule whose whole job is to take a state back. An axis left unstated is an
|
|
768 |
+ |
/// axis the state above keeps, so `Flat` re-asserted nothing at all and a
|
|
769 |
+ |
/// disabled control kept whatever hover had given it.
|
|
770 |
+ |
///
|
|
771 |
+ |
/// So the axes the depth is silent on are withdrawn rather than skipped, and
|
|
772 |
+ |
/// the withdrawal is spelled by [`reset`] rather than here, so a disabled
|
|
773 |
+ |
/// control and a flat one say the same words. Reaches further than the fill:
|
|
774 |
+ |
/// [`Depth::Sunken`] and [`Depth::Overlay`] have no bevel either, and the
|
|
775 |
+ |
/// pressed rule above hands out an inset one.
|
|
776 |
+ |
fn rest_declarations(depth: Depth) -> String {
|
|
777 |
+ |
let mut css = String::new();
|
|
778 |
+ |
match depth.fill() {
|
|
779 |
+ |
Some(fill) => {
|
|
780 |
+ |
let _ = writeln!(css, " background: {};", fill_var(fill));
|
|
781 |
+ |
}
|
|
782 |
+ |
None => css.push_str(&Reset::NOTHING.and(Chrome::Fill).declarations()),
|
|
783 |
+ |
}
|
|
784 |
+ |
match depth.bevel() {
|
|
785 |
+ |
Some(bevel) => {
|
|
786 |
+ |
let _ = writeln!(css, " box-shadow: var({});", bevel_var(bevel));
|
|
787 |
+ |
}
|
|
788 |
+ |
None => css.push_str(&Reset::NOTHING.and(Chrome::Shadow).declarations()),
|
|
789 |
+ |
}
|
|
790 |
+ |
css
|
|
791 |
+ |
}
|
|
792 |
+ |
|
| 760 |
793 |
|
/// Present, visible, and not answering.
|
| 761 |
794 |
|
///
|
| 762 |
795 |
|
/// Matches the ARIA attribute as well as the pseudo-class, because `:disabled`
|
| 772 |
805 |
|
/// caller emits is (0,2,0), and adding a `:not(:disabled)` anywhere would raise
|
| 773 |
806 |
|
/// one of them and have to be unpicked when this output moves inside its own
|
| 774 |
807 |
|
/// cascade layer.
|
|
808 |
+ |
///
|
|
809 |
+ |
/// Re-asserted on **both** axes, through [`rest_declarations`], which is the
|
|
810 |
+ |
/// half this got wrong until 0.68.0. `depth_declarations` alone is empty for
|
|
811 |
+ |
/// [`Depth::Flat`], so the three flat controls this crate emits -- both facet
|
|
812 |
+ |
/// arms and a suggestion entry -- won the contest with nothing to say and kept
|
|
813 |
+ |
/// the hover surface underneath a control that had stopped answering.
|
| 775 |
814 |
|
#[must_use]
|
| 776 |
815 |
|
pub fn disabled_rule(selector: &str, depth: Depth) -> String {
|
| 777 |
816 |
|
format!(
|
| 778 |
817 |
|
".{selector}:disabled,\n.{selector}[aria-disabled=\"true\"] {{\n{} color: var(--{});\n cursor: not-allowed;\n}}\n",
|
| 779 |
|
- |
depth_declarations(depth),
|
|
818 |
+ |
rest_declarations(depth),
|
| 780 |
819 |
|
State::Disabled.token()
|
| 781 |
820 |
|
)
|
| 782 |
821 |
|
}
|
| 2354 |
2393 |
|
assert!(tail.contains("background: var(--surface-raised)"));
|
| 2355 |
2394 |
|
}
|
| 2356 |
2395 |
|
|
|
2396 |
+ |
#[test]
|
|
2397 |
+ |
fn a_flat_control_takes_its_hover_fill_back_when_it_stops_answering() {
|
|
2398 |
+ |
// The same contest one depth over, and the half `depth_declarations`
|
|
2399 |
+ |
// could not state. Flat declares neither axis, so before 0.68.0 the
|
|
2400 |
+ |
// disabled rule won on source order with nothing to say and the hover
|
|
2401 |
+ |
// surface stayed under a control that had stopped answering. Reaches
|
|
2402 |
+ |
// both facet arms and a suggestion entry.
|
|
2403 |
+ |
for depth in [Depth::Flat, Depth::Sunken, Depth::Overlay] {
|
|
2404 |
+ |
let css = disabled_rule("x", depth);
|
|
2405 |
+ |
assert!(css.contains("box-shadow"), "{depth:?}: {css}");
|
|
2406 |
+ |
}
|
|
2407 |
+ |
let flat = disabled_rule("x", Depth::Flat);
|
|
2408 |
+ |
assert!(flat.contains("background: none;"), "{flat}");
|
|
2409 |
+ |
assert!(flat.contains("box-shadow: none;"), "{flat}");
|
|
2410 |
+ |
|
|
2411 |
+ |
// Every rule the caller emits states both axes now, so whichever wins
|
|
2412 |
+ |
// the source-order contest leaves nothing of the one below it.
|
|
2413 |
+ |
let css = interactive_rules("x", Depth::Flat, &Emit::default());
|
|
2414 |
+ |
let disabled = css.find(":disabled").expect("disabled");
|
|
2415 |
+ |
assert!(css[disabled..].contains("background: none;"), "{css}");
|
|
2416 |
+ |
}
|
|
2417 |
+ |
|
| 2357 |
2418 |
|
#[test]
|
| 2358 |
2419 |
|
fn a_disabled_state_reaches_things_that_cannot_be_disabled() {
|
| 2359 |
2420 |
|
// `:disabled` matches form elements only, and a chip is a div. Keying
|