1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
//! The classes a list's or a table's columns, rows and cells carry.
//!
//! The row and cell markup is drawn by the quasi hosts (`quasi-webview` for
//! this renderer), which own the column and cell members of the vocabulary.
//! What stays here is the naming those hosts write into markup and the
//! generated stylesheet rules: a column's name as a class
//! ([`push_column_name`]), the kind, part and flow classes, the cell wrapper
//! ([`CELL_IN`]) and sizer ([`push_sizer`]), the `short-N`, `stacks-N` and
//! `drop-*-N` ladders ([`SHORT_RUNGS`], [`STACKS_RUNGS`], [`DROP_RUNGS`]), and
//! the class lists the vocabulary is checked against.
//!
//! # Why positions are the bug
//!
//! goingson hid its mobile columns with `nth-child(n+5)` against a
//! seven-column table, plus a separate `nth-child(3)`, plus two class-based
//! rules, the same fact said three ways, two of them positional. Insert a
//! column anywhere left of the cut and the wrong one disappears, silently,
//! because nothing in the stylesheet knows what column five *is*.
//! [`Priority`](makeover_layout::Priority) is the fix: a renderer narrows by what a column is worth, as a
//! shrink factor on the column's own class (one of [`CELL_DROP_CLASSES`]), and
//! never by counting.
use crateMarkup;
use crate::;
use ;
/// A column's name as the identifier half of its class.
///
/// # Why this is not escaping
///
/// The name is the one app-supplied string this crate puts in a class attribute
/// rather than in text or an `aria-label`, so it may never go in raw. A column
/// named `a" onclick="steal()` emitted
///
/// ```html
/// <div class="cell col-a" onclick="steal() cell-fill cell-keeps">
/// ```
///
/// which is a live event handler on every cell of that column. HTML escaping is
/// the reflex and it is the wrong tool here, because a class is read twice: once
/// by the HTML parser, which would decode `"` back to a quote, and once by
/// the CSS selector an app writes against it. An escaped name is safe in the
/// attribute and unmatchable from the stylesheet, so the two halves would stop
/// meeting, silently, the way every other defect this module's comments
/// record did.
///
/// Reducing the name to identifier characters answers both. What comes out is a
/// valid CSS identifier, so the selector matches, and it holds none of the five
/// characters an attribute value can be ended with, so there is nothing to
/// escape.
///
/// # What it changes for a name that was already fine
///
/// Nothing. Alphanumerics, `_` and `-` pass through, and every column name in
/// the tree is made of those. A name that is *not* was already broken rather
/// than merely unsafe: `Due date` emitted `col-Due date`, which the HTML parser
/// reads as the two classes `col-Due` and `date`, and which a selector wrote as
/// a descendant selector that matched neither. Both now agree on
/// `col-Due-date`.
///
/// Alphanumeric in the Unicode sense, not the ASCII one. CSS identifiers admit
/// everything from U+00A0 up, so a column named `Größe` keeps its name; folding
/// it to `Gr--e` would collide with a neighbouring column for nothing.
/// The class saying what a column holds, or `None` for prose.
///
/// wiki `table-model`: the kind carries the look, so the class goes on every
/// cell and on the heading, and the sheet rules each kind once. Text is the
/// absence of a kind, as `cell-keeps` would be if a keep needed saying, so it
/// writes nothing. A kind added upstream reads as text: plain, never missing.
/// Every class [`kind_class`] can return.
pub const KIND_CLASSES: & = &;
/// The wrapper every cell and heading holds its contents in.
///
/// The column is a flex item that may close to nothing, and padding cannot
/// shrink, so the padding lives on this and the column clips it. It is also
/// what a droppable column hides when it is under its floor, and where the
/// type goes, so a heading and the cells under it are the same box whatever
/// each holds.
pub const CELL_IN: &str = "cell-in";
/// What a kept column is as wide as, when its floor cannot say.
///
/// A floor is a count of `ch`, and a column of controls is not drawn in `ch`.
/// Measured on MNW's tables at 1440 and 420: a label's letters run 6 to 8px
/// against the 9px rung while a button's padding and edge take 26px against the
/// 18 that `quasi_router::screen::ACT_ROOM` allows, so "Add to cart" held 150px
/// to draw 118 and "Remove" held 96 to draw 99. The first starved the name
/// beside it to 32px at 420; the second clipped at every width. No constant
/// fixes both, because the error is the face's and not the count's.
///
/// So the host that knows what a column holds writes a copy of it here, once
/// per distinct run of controls, drawn in the row's own type and taking no
/// height and no ink. A kept column holding one is as wide as its widest copy,
/// which the browser measures in the face it draws, and every cell and the
/// heading hold the same copies, so the column is one width down the table.
/// Hidden rather than absent: a hidden box still has a width.
///
/// Write it with [`push_sizer`] and never by hand, so the wrapper the sheet
/// rules and the one a host emits cannot be spelled apart.
pub const CELL_SIZER: &str = "cell-sizer";
/// A [`CELL_SIZER`] around `copies`, which go inside a cell's or a heading's
/// [`CELL_IN`] after what it holds.
///
/// `copies` is the host's: one block per distinct run, each drawn with the
/// classes and the element the real controls take, since an element rule an
/// app writes for `button` reaches a copy only if it is one. Nothing in it may
/// be a control anyone can reach: `aria-hidden` here and `visibility: hidden`
/// in the sheet take it out of the accessibility tree and the tab order, and a
/// copy carries no address. Empty writes nothing.
///
/// [`Markup`], because the copies are the host's markup and are written as-is.
/// Every class [`part_class`] can return, including the fallback.
///
/// Beside the match rather than derived from it, because a `match` over a
/// `#[non_exhaustive]` enum cannot be enumerated from outside. It carries the
/// same obligation the match does and a test below holds the two together, so
/// a new arm added without a new entry fails rather than silently narrowing
/// what a checker believes this crate can emit.
pub const ROW_PART_CLASSES: & = &;
/// Every class [`flow_class`] can return.
///
/// `Flow::Tight` has no class: one line is what a run already does, so a rule
/// for it would restate the default on every part in every row. Only the tier
/// that departs from it is named, which is also why a renderer emitting nothing
/// for an unknown flow is correct rather than lossy.
pub const FLOW_CLASSES: & = &;
/// The classes a row inside a hierarchy carries.
///
/// Declared here because the generated sheet defines rules for them: the
/// vocabulary has to say so, or the check that every emitted name has a rule
/// cannot see them.
pub const NESTING_CLASSES: & = &;
/// The class for a part's flow, if it needs one.
///
/// `None` for [`Flow::Tight`] and for any tier added upstream that this crate
/// has not been taught, which lands as one line: the same trade
/// [`part_class`]'s fallback makes, and the safe direction, since a part that
/// grows without bound breaks the rows around it while a part that stays on one
/// line only looks like the old rendering. Grep this when adopting a new
/// makeover-layout.
/// Every class saying how wide a cell of a column asks to be, including the
/// fill a column that makes no claim lands on.
///
/// See [`ROW_PART_CLASSES`] for why it is written out. Only two of the three
/// carry a rule, a fill is what a cell does when the sheet says nothing,
/// which is exactly why the list is here rather than being read off the
/// generated CSS: `cell-fill` reached every table in the tree and the
/// vocabulary named it nowhere.
pub const CELL_WIDTH_CLASSES: & = &;
/// Every class saying when a cell of a column drops, including the tier that
/// never does.
///
/// The class is a shrink factor: a table short of room takes the shortfall
/// from the columns worth least, and nothing counts positions or names a
/// breakpoint.
///
/// [`CELL_WIDTH_CLASSES`]' argument, one column property over: `cell-keeps` is
/// the tier the narrowing never hides, so the sheet writes no rule for it and
/// a scraped set cannot see it.
pub const CELL_DROP_CLASSES: & = &;
/// The rungs of the `short-N` ladder, in `rem`: the narrowest and widest table
/// width it can say, and the step between two.
///
/// Two `rem` a step, so a table is marked short at most 32px before its
/// essentials actually stop fitting, which is well inside the one gap of slack
/// every floor already carries. A table needing more than the ceiling is wider
/// than any pane the apps draw, and gets no class.
pub const SHORT_RUNGS: = ;
/// The rungs of the `stacks-N` ladder, in `rem`: the width under which a
/// table's rows stop being a grid and draw as stacked records.
///
/// The [`SHORT_RUNGS`] ladder, kept a name of its own because a table can carry
/// a rung of each. One that folds its acts into a row menu is short below the
/// width its essentials need with the acts in them, and stacks below the
/// narrower width they need with the acts folded away; the host works out both
/// from the same floors.
pub const STACKS_RUNGS: = SHORT_RUNGS;
/// The rungs of the `drop-optional-N` and `drop-secondary-N` ladders, in
/// `rem`: the width under which a table's optional, then its secondary,
/// columns close outright.
///
/// The [`SHORT_RUNGS`] ladder again. A host puts the optional rung at every
/// column's floor added up and the secondary rung at the floors of the
/// essential and secondary columns, which is the budget the terminal and egui
/// raise their cutoff by.
pub const DROP_RUNGS: = SHORT_RUNGS;
/// Every class [`cell_part_class`] can return, including the fallback.
///
/// See [`ROW_PART_CLASSES`] for why it is written out.
pub const CELL_PART_CLASSES: & = &;
/// The class for a row part.
///
/// [`RowPart`] is `#[non_exhaustive]`, so a member added upstream does not stop
/// this compiling.
///
/// The fallback is what that costs. A member added upstream lands here as a
/// bare `row-part`, which the sheet lets shrink like its siblings and otherwise
/// leaves plain: a thing rendering plainly rather than a build that stops. Grep
/// this function when adopting a new makeover-layout.
///
/// Public, because a row's parts are emitted by whoever builds the row element
/// and that is not always the same host: a table's cells are one emitter, but a
/// list row carries the app's identity and hooks, so a screen renderer writes
/// it. A renderer that reimplements this list rather than calling it takes on
/// the obligation above without knowing it.
/// The class for a cell part.
///
/// [`part_class`]'s table half, for [`CellPart`]. The fallback is there for the
/// same reason and costs the same thing: a member added upstream lands as a
/// bare `cell-part` with no rule of its own, rather than as a build that stops.
/// Grep this function too when adopting a new makeover-layout. Public for the
/// reason [`part_class`] is.