use crate::list::{self, cell_part_class};
use crate::{Emit, class, gated, hover_condition};
use makeover_geometry::{Density, Gap};
use makeover_layout::CellPart;
use std::fmt::Write as _;
pub(crate) const CH_REM: f32 = 0.5625;
const FILL_GROW: u8 = 4;
const TABLE_TYPE: &str = "var(--text-note)";
pub(crate) fn table_rules(opts: &Emit) -> String {
let table = class("table", opts);
let head = class("table-head", opts);
let row = class("table-row", opts);
let heading = class("table-heading", opts);
let cell = class("cell", opts);
let mut css = String::new();
let bw = opts.border_width;
let cell_in = class("cell-in", opts);
let _ = writeln!(
css,
".{table} {{\n border: {bw} solid var(--row-rule);\n \
background: var(--surface-raised);\n font-size: {TABLE_TYPE};\n \
overflow-x: clip;\n}}"
);
let _ = writeln!(
css,
".{table} .{head},\n.{table} .{row} {{\n display: flex;\n \
min-inline-size: var(--table-stacked, max-content) var(--table-grid, 0);\n}}"
);
let _ = writeln!(
css,
".{table} .{row} {{\n min-height: var(--row-block);\n \
flex-wrap: var(--table-stacked, nowrap) var(--table-grid, wrap);\n \
padding-block: var(--table-stacked, 0) var(--table-grid, var(--gap-bound));\n}}"
);
let _ = writeln!(
css,
".{table} .{head} {{\n \
position: var(--table-stacked, sticky) var(--table-grid, absolute);\n \
inset-block-start: var(--table-head-offset, 0);\n \
z-index: 1;\n \
inline-size: var(--table-stacked, auto) var(--table-grid, 1px);\n \
block-size: var(--table-stacked, auto) var(--table-grid, 1px);\n \
overflow: var(--table-stacked, visible) var(--table-grid, hidden);\n \
clip-path: var(--table-stacked, none) var(--table-grid, inset(50%));\n}}"
);
let _ = writeln!(
css,
".{table} .{heading},\n.{table} .{cell} {{\n display: flex;\n \
align-items: center;\n flex: 0 1 calc({}rem + 2 * var(--gap-group));\n \
min-width: 0;\n overflow: hidden;\n container-type: inline-size;\n}}",
8.0 * CH_REM
);
let _ = writeln!(
css,
".{table} .{cell_in} {{\n flex: 1 1 auto;\n min-width: 0;\n \
overflow: hidden;\n text-overflow: ellipsis;\n}}"
);
let _ = writeln!(
css,
".{table} .{head} {{\n color: var(--content-secondary);\n \
background: var(--surface-sunken);\n border-bottom: {bw} solid var(--bevel-dark);\n}}"
);
let _ = writeln!(
css,
".{table} .{heading} {{\n align-items: flex-end;\n \
background: var(--surface-sunken);\n}}"
);
let _ = writeln!(
css,
".{table} .{heading} > .{cell_in} {{\n padding: var(--gap-peer) var(--gap-group);\n \
font-size: var(--text-fine);\n font-weight: bold;\n \
letter-spacing: var(--table-heading-tracking, 0.06em);\n \
text-transform: uppercase;\n text-align: start;\n white-space: nowrap;\n}}"
);
let _ = writeln!(
css,
".{table} .{row} > .{cell} > .{cell_in} {{\n padding: var(--gap-bound) var(--gap-group);\n}}"
);
let _ = writeln!(
css,
".{table} .{row} + .{row} {{\n border-top: {bw} solid var(--row-rule);\n}}"
);
css.push_str(&gated(
hover_condition(),
&format!(".{table} .{row}:hover {{\n background: var(--row-hover);\n}}\n"),
));
let [
kind_identifier,
kind_date,
kind_number,
kind_status,
kind_actions,
kind_code,
] = [
"kind-identifier",
"kind-date",
"kind-number",
"kind-status",
"kind-actions",
"kind-code",
]
.map(|name| class(name, opts));
let _ = writeln!(
css,
".{table} .{cell}.{kind_identifier} > .{cell_in} {{\n font-family: var(--font-mono);\n \
font-size: var(--text-fine);\n overflow-wrap: anywhere;\n}}"
);
let _ = writeln!(
css,
".{table} .{kind_date} > .{cell_in} {{\n white-space: nowrap;\n}}\n\
.{table} .{cell}.{kind_date} > .{cell_in} {{\n font-variant-numeric: tabular-nums;\n}}"
);
let _ = writeln!(
css,
".{table} .{kind_number} > .{cell_in} {{\n white-space: nowrap;\n text-align: var(--table-stacked, end) var(--table-grid, start);\n}}\n\
.{table} .{cell}.{kind_number} > .{cell_in} {{\n font-variant-numeric: tabular-nums;\n}}"
);
let _ = writeln!(
css,
".{table} .{kind_status} > .{cell_in} {{\n white-space: nowrap;\n}}"
);
let _ = writeln!(
css,
".{table} .{kind_actions} > .{cell_in} {{\n white-space: nowrap;\n text-align: var(--table-stacked, end) var(--table-grid, start);\n}}"
);
let _ = writeln!(
css,
".{table} .{cell}.{kind_code} > .{cell_in} {{\n font-family: var(--font-mono);\n \
white-space: pre;\n}}"
);
let code = format!(".{table}:has(.{kind_code})");
let _ = writeln!(css, "{code} {{\n overflow-x: auto;\n}}");
let _ = writeln!(
css,
"{code} {{\n display: grid;\n grid-template-columns: minmax(100%, max-content);\n}}"
);
let _ = writeln!(
css,
"{code} .{row} {{\n min-height: auto;\n align-items: baseline;\n}}\n\
{code} .{row} + .{row} {{\n border-top: 0;\n}}\n\
{code} .{row} > .{cell} > .{cell_in} {{\n padding-block: 0;\n}}\n\
{code} .{row} > .{cell}.{kind_code},\n{code} .{head} > .{heading}.{kind_code} {{\n \
flex: 1 0 auto;\n max-inline-size: none;\n overflow: visible;\n container-type: normal;\n}}\n\
{code} .{row} > .{cell}:not(.{kind_code}),\n{code} .{head} > .{heading}:not(.{kind_code}) {{\n \
flex-grow: 0;\n}}"
);
css.push_str(&gated(
hover_condition(),
&format!("{code} .{row}:hover {{\n background: none;\n}}\n"),
));
let _ = writeln!(
css,
".{table} .{} {{\n flex-grow: {FILL_GROW};\n max-inline-size: var(--measure-reading, 42rem);\n}}",
class("cell-fill", opts)
);
let _ = writeln!(
css,
".{table} .{},\n.{table} .{} {{\n flex-grow: 1;\n}}",
class("cell-content", opts),
class("cell-fixed", opts)
);
let _ = writeln!(
css,
".{table} .{} > .{cell_in} {{\n overflow-wrap: anywhere;\n}}",
class("cell-fill", opts)
);
let _ = writeln!(
css,
".{table} .{} > .{cell_in},\n.{table} .{} > .{cell_in} {{\n white-space: nowrap;\n}}",
class("cell-content", opts),
class("cell-fixed", opts)
);
let [drops_first, drops_next, keeps] =
["cell-drops-first", "cell-drops-next", "cell-keeps"].map(|name| class(name, opts));
let _ = writeln!(
css,
".{table} .{drops_first} {{\n flex-shrink: 1000000;\n}}\n\
.{table} .{drops_next} {{\n flex-shrink: 1000;\n}}"
);
let _ = writeln!(css, ".{table} .{keeps} {{\n flex-shrink: 0;\n}}");
let rungs = (2..=makeover_layout::MIN_CEILING).step_by(2);
let fill = class("cell-fill", opts);
for n in rungs.clone() {
let min = class(&format!("min-{n}"), opts);
let basis = f32::from(n) * CH_REM;
let _ = writeln!(
css,
".{table} .{row} > .{kind_actions}.{min} {{\n \
flex-basis: var(--table-stacked, calc({basis}rem + 2 * var(--gap-group))) var(--table-grid, auto);\n}}\n\
.{table} .{row} > .{fill}.{min} {{\n \
flex-basis: calc({basis}rem + 2 * var(--gap-group));\n}}"
);
}
let sizer = class(list::CELL_SIZER, opts);
let _ = writeln!(
css,
".{table} .{keeps}:has(> .{cell_in} > .{sizer}) {{\n flex-basis: auto;\n \
container-type: normal;\n}}\n\
.{table} .{sizer} {{\n display: block;\n block-size: 0;\n overflow: hidden;\n \
visibility: hidden;\n white-space: nowrap;\n font-size: {TABLE_TYPE};\n \
font-weight: normal;\n letter-spacing: normal;\n text-transform: none;\n}}\n\
.{table} .{sizer} > * {{\n display: flex;\n inline-size: max-content;\n}}"
);
for n in rungs.clone() {
let _ = writeln!(
css,
".{table} .{} {{\n flex-basis: var(--table-stacked, calc({}rem + 2 * var(--gap-group))) var(--table-grid, 100%);\n}}",
class(&format!("min-{n}"), opts),
f32::from(n) * CH_REM
);
}
for &density in Density::all() {
let step = Gap::Group.step_at(density).ratio();
let edge = f32::from(step.numerator) / f32::from(step.denominator);
let mut block = String::new();
for n in rungs.clone() {
let _ = writeln!(
block,
"@container (inline-size < {}rem) {{\n \
.{table} :is(.{drops_first}, .{drops_next}).{} > .{cell_in} {{\n \
visibility: hidden;\n max-block-size: 0;\n overflow: hidden;\n }}\n}}",
f32::from(n) * CH_REM + edge,
class(&format!("min-{n}"), opts)
);
let _ = writeln!(
block,
"@container (inline-size < {}rem) {{\n \
.{table} .{keeps}.{fill}.{} > .{cell_in} {{\n \
white-space: nowrap;\n overflow-wrap: normal;\n }}\n}}",
f32::from(n) * CH_REM + edge,
class(&format!("min-{n}"), opts),
fill = class("cell-fill", opts),
);
}
match density {
Density::Pointer => css.push_str(&block),
Density::Touch => css.push_str(&gated(Some(density.media_condition()), &block)),
}
}
let _ = writeln!(
css,
".{table} > * {{\n --table-roomy: ;\n --table-grid: ;\n}}"
);
let (low, high, step) = list::SHORT_RUNGS;
for n in (low..=high).step_by(usize::from(step)) {
let short = class(&format!("short-{n}"), opts);
let _ = writeln!(
css,
".{table}.{short} {{\n container: table-frame / inline-size;\n}}\n\
@container table-frame (inline-size < {n}rem) {{\n \
.{table}.{short} > * {{\n --table-short: ;\n \
--table-roomy: initial;\n }}\n}}"
);
}
let [optional, secondary] = ["optional", "secondary"];
let _ = writeln!(
css,
".{table} .{drops_first} {{\n max-inline-size: var(--table-{optional}-closed, none);\n}}\n\
.{table} .{drops_next} {{\n max-inline-size: var(--table-{secondary}-closed, none);\n}}"
);
let (low, high, step) = list::DROP_RUNGS;
for tier in [optional, secondary] {
for n in (low..=high).step_by(usize::from(step)) {
let drop = class(&format!("drop-{tier}-{n}"), opts);
let _ = writeln!(
css,
".{table}.{drop} {{\n container: table-frame / inline-size;\n}}\n\
@container table-frame (inline-size < {n}rem) {{\n \
.{table}.{drop} > * {{\n --table-{tier}-closed: 0;\n }}\n}}"
);
}
}
let (low, high, step) = list::STACKS_RUNGS;
for n in (low..=high).step_by(usize::from(step)) {
let stacks = class(&format!("stacks-{n}"), opts);
let _ = writeln!(
css,
".{table}.{stacks} {{\n container: table-frame / inline-size;\n}}\n\
@container table-frame (inline-size < {n}rem) {{\n \
.{table}.{stacks} > * {{\n --table-stacked: ;\n \
--table-grid: initial;\n --table-short: initial;\n \
--table-roomy: ;\n --table-optional-closed: initial;\n \
--table-secondary-closed: initial;\n }}\n}}"
);
}
let _ = writeln!(
css,
".{table} .{row} > .{cell} {{\n \
align-items: var(--table-stacked, center) var(--table-grid, baseline);\n}}\n\
.{table} .{row} > .{fill} {{\n order: var(--table-stacked, 0) var(--table-grid, -2);\n}}\n\
.{table} .{row}::after {{\n content: var(--table-stacked, none) var(--table-grid, \"\");\n \
flex-basis: 100%;\n order: -1;\n}}\n\
.{table} .{row} > .{cell}[data-heading]::before {{\n \
content: var(--table-stacked, none) var(--table-grid, attr(data-heading));\n \
flex: 0 0 var(--table-record-label, 40%);\n padding-inline-start: var(--gap-group);\n \
color: var(--content-secondary);\n font-size: var(--text-fine);\n \
font-weight: bold;\n letter-spacing: var(--table-heading-tracking, 0.06em);\n \
text-transform: uppercase;\n overflow-wrap: anywhere;\n}}"
);
let tiles = class("table-tiles", opts);
let always = class("tiles-always", opts);
let picture = class("kind-picture", opts);
let drops_first = class("cell-drops-first", opts);
let tile_floor = f32::from(makeover_layout::ColumnKind::Picture.floor()) * CH_REM;
let on = "--tiled-on: ;\n --tiled-off: initial;\n --table-secondary-closed: initial;";
let _ = writeln!(css, ".{tiles}.{always} > .{table} {{\n {on}\n}}");
for n in (low..=high).step_by(usize::from(step)) {
let rung = class(&format!("tiles-{n}"), opts);
let _ = writeln!(
css,
".{tiles}.{rung} {{\n container: tile-frame / inline-size;\n}}\n\
@container tile-frame (inline-size < {n}rem) {{\n \
.{tiles}.{rung} > .{table} {{\n {on}\n }}\n}}"
);
}
let _ = writeln!(
css,
".{tiles} > .{table} {{\n \
display: var(--tiled-off, block) var(--tiled-on, grid);\n \
grid-template-columns: repeat(auto-fill, minmax(min(100%, calc({tile_floor}rem + 2 * var(--gap-group))), 1fr));\n \
gap: var(--tiled-off, 0) var(--tiled-on, var(--gap-group));\n \
border-color: var(--tiled-off, var(--row-rule)) var(--tiled-on, transparent);\n \
background: var(--tiled-off, var(--surface-raised)) var(--tiled-on, transparent);\n}}\n\
.{tiles} > .{table} > .{head} {{\n display: var(--tiled-off, flex) var(--tiled-on, none);\n}}\n\
.{tiles} > .{table} > .{row} {{\n \
flex-direction: var(--tiled-off, row) var(--tiled-on, column);\n \
min-inline-size: var(--tiled-off, max-content) var(--tiled-on, 0);\n \
gap: var(--tiled-off, 0) var(--tiled-on, var(--gap-bound));\n \
padding-block: var(--tiled-off, 0) var(--tiled-on, 0 var(--gap-group));\n}}\n\
.{tiles} > .{table} > .{row} > .{cell} {{\n flex: var(--tiled-off, 0 1 auto) var(--tiled-on, none);\n}}\n\
.{tiles} > .{table} > .{row} > .{drops_first} {{\n display: var(--tiled-off, flex) var(--tiled-on, none);\n}}\n\
.{tiles} > .{table} > .{row} > .{fill} {{\n \
order: var(--tiled-off, 0) var(--tiled-on, -1);\n \
font-weight: var(--tiled-off, inherit) var(--tiled-on, var(--weight-strong));\n}}\n\
.{tiles} .{picture} {{\n display: var(--tiled-off, none) var(--tiled-on, flex);\n order: -2;\n}}\n\
.{tiles} .{picture} img {{\n \
inline-size: 100%;\n aspect-ratio: 1;\n object-fit: cover;\n \
border-radius: var(--radius-control);\n}}"
);
for part in [
CellPart::Value,
CellPart::Tokens,
CellPart::Actions,
CellPart::Link,
] {
if !matches!(part, CellPart::Tokens | CellPart::Actions) {
let underline = if part == CellPart::Link {
"\n text-decoration: none;"
} else {
""
};
let _ = writeln!(
css,
".{} {{\n color: var(--{});{underline}\n}}",
class(cell_part_class(part), opts),
part.intent()
);
}
}
css
}