max / goingson
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
4 files changed,
+159 insertions,
-44 deletions
| @@ -53,7 +53,81 @@ | |||
| 53 | 53 | }, | |
| 54 | 54 | ]; | |
| 55 | 55 | ||
| 56 | - | /// Generate the two tables' column CSS. | |
| 56 | + | /// The upcoming-events table, left to right. | |
| 57 | + | /// | |
| 58 | + | /// Separate from [`RECURRING_COLUMNS`] because these are two tables and not | |
| 59 | + | /// one. They had been sharing a class and therefore a grid, which is what put | |
| 60 | + | /// the upcoming row's six cells into five tracks: the checkbox took the date's | |
| 61 | + | /// track, the title took the location's, and the location took the 40px one | |
| 62 | + | /// meant for the kebab. Only the recurring table ever fitted. | |
| 63 | + | const UPCOMING_COLUMNS: &[Column<'static>] = &[ | |
| 64 | + | // Bulk selection. Goes first on a narrow screen, like the kebab does. | |
| 65 | + | Column { | |
| 66 | + | name: "select", | |
| 67 | + | width: Width::Fixed, | |
| 68 | + | priority: Priority::Optional, | |
| 69 | + | }, | |
| 70 | + | Column { | |
| 71 | + | name: "date", | |
| 72 | + | width: Width::Fixed, | |
| 73 | + | priority: Priority::Essential, | |
| 74 | + | }, | |
| 75 | + | Column { | |
| 76 | + | name: "time", | |
| 77 | + | width: Width::Fixed, | |
| 78 | + | priority: Priority::Secondary, | |
| 79 | + | }, | |
| 80 | + | Column { | |
| 81 | + | name: "title", | |
| 82 | + | width: Width::Fill, | |
| 83 | + | priority: Priority::Essential, | |
| 84 | + | }, | |
| 85 | + | Column { | |
| 86 | + | name: "location", | |
| 87 | + | width: Width::Fixed, | |
| 88 | + | priority: Priority::Optional, | |
| 89 | + | }, | |
| 90 | + | Column { | |
| 91 | + | name: "actions", | |
| 92 | + | width: Width::Fixed, | |
| 93 | + | priority: Priority::Optional, | |
| 94 | + | }, | |
| 95 | + | ]; | |
| 96 | + | ||
| 97 | + | /// The recurring-events table, left to right. | |
| 98 | + | /// | |
| 99 | + | /// Its first column is the recurrence pattern where the upcoming table's is a | |
| 100 | + | /// date, and it has no bulk selection, which is the other half of why one | |
| 101 | + | /// description could never have served both. | |
| 102 | + | const RECURRING_COLUMNS: &[Column<'static>] = &[ | |
| 103 | + | Column { | |
| 104 | + | name: "pattern", | |
| 105 | + | width: Width::Fixed, | |
| 106 | + | priority: Priority::Essential, | |
| 107 | + | }, | |
| 108 | + | Column { | |
| 109 | + | name: "time", | |
| 110 | + | width: Width::Fixed, | |
| 111 | + | priority: Priority::Secondary, | |
| 112 | + | }, | |
| 113 | + | Column { | |
| 114 | + | name: "title", | |
| 115 | + | width: Width::Fill, | |
| 116 | + | priority: Priority::Essential, | |
| 117 | + | }, | |
| 118 | + | Column { | |
| 119 | + | name: "location", | |
| 120 | + | width: Width::Fixed, | |
| 121 | + | priority: Priority::Optional, | |
| 122 | + | }, | |
| 123 | + | Column { | |
| 124 | + | name: "actions", | |
| 125 | + | width: Width::Fixed, | |
| 126 | + | priority: Priority::Optional, | |
| 127 | + | }, | |
| 128 | + | ]; | |
| 129 | + | ||
| 130 | + | /// Generate the tables' column CSS. | |
| 57 | 131 | /// | |
| 58 | 132 | /// Both halves of narrowing come out of one call per breakpoint: the track list | |
| 59 | 133 | /// and the hiding. They used to be written apart and kept in step by hand, | |
| @@ -82,6 +156,37 @@ | |||
| 82 | 156 | fallback: "", | |
| 83 | 157 | }; | |
| 84 | 158 | ||
| 159 | + | let upcoming_wide = Sizing { | |
| 160 | + | lengths: &[ | |
| 161 | + | ("select", "40px"), | |
| 162 | + | ("date", "100px"), | |
| 163 | + | ("time", "80px"), | |
| 164 | + | ("title", "0"), | |
| 165 | + | ("location", "150px"), | |
| 166 | + | ("actions", "40px"), | |
| 167 | + | ], | |
| 168 | + | fallback: "", | |
| 169 | + | }; | |
| 170 | + | let upcoming_narrow = Sizing { | |
| 171 | + | lengths: &[("date", "90px"), ("time", "70px"), ("title", "0")], | |
| 172 | + | fallback: "", | |
| 173 | + | }; | |
| 174 | + | ||
| 175 | + | let recurring_wide = Sizing { | |
| 176 | + | lengths: &[ | |
| 177 | + | ("pattern", "140px"), | |
| 178 | + | ("time", "80px"), | |
| 179 | + | ("title", "0"), | |
| 180 | + | ("location", "150px"), | |
| 181 | + | ("actions", "40px"), | |
| 182 | + | ], | |
| 183 | + | fallback: "", | |
| 184 | + | }; | |
| 185 | + | let recurring_narrow = Sizing { | |
| 186 | + | lengths: &[("pattern", "110px"), ("time", "70px"), ("title", "0")], | |
| 187 | + | fallback: "", | |
| 188 | + | }; | |
| 189 | + | ||
| 85 | 190 | let mut css = String::from( | |
| 86 | 191 | "/* Generated by makeover-webview from the column descriptions in\n \ | |
| 87 | 192 | build.rs. Do not edit. Columns narrow by priority, never by position:\n \ | |
| @@ -89,18 +194,29 @@ | |||
| 89 | 194 | silently disappears. */\n", | |
| 90 | 195 | ); | |
| 91 | 196 | ||
| 92 | - | // Tasks only for now. The events table is not describable as one column | |
| 93 | - | // set yet: its two row builders disagree, the upcoming row emits six cells | |
| 94 | - | // and the recurring row five, against a header of five and a five-track | |
| 95 | - | // grid they share. Emitting narrowing rules for a table whose markup does | |
| 96 | - | // not match them would hide nothing and shorten the tracks, which is worse | |
| 97 | - | // than the hand-written rules it would replace. | |
| 98 | - | for (selector, columns, wide, narrow) in [( | |
| 99 | - | ".task-header-row, .task-row", | |
| 100 | - | TASK_COLUMNS, | |
| 101 | - | &task_wide, | |
| 102 | - | &task_narrow, | |
| 103 | - | )] { | |
| 197 | + | // The two event selectors carry a modifier rather than a class of their | |
| 198 | + | // own, so everything the two tables genuinely share -- padding, hover, the | |
| 199 | + | // row border -- stays on .event-row-virtual and only the grid splits. | |
| 200 | + | for (selector, columns, wide, narrow) in [ | |
| 201 | + | ( | |
| 202 | + | ".task-header-row, .task-row", | |
| 203 | + | TASK_COLUMNS, | |
| 204 | + | &task_wide, | |
| 205 | + | &task_narrow, | |
| 206 | + | ), | |
| 207 | + | ( | |
| 208 | + | ".event-header-row.event-header-upcoming, .event-row-virtual.event-upcoming", | |
| 209 | + | UPCOMING_COLUMNS, | |
| 210 | + | &upcoming_wide, | |
| 211 | + | &upcoming_narrow, | |
| 212 | + | ), | |
| 213 | + | ( | |
| 214 | + | ".event-header-row.event-header-recurring, .event-row-virtual.event-recurring", | |
| 215 | + | RECURRING_COLUMNS, | |
| 216 | + | &recurring_wide, | |
| 217 | + | &recurring_narrow, | |
| 218 | + | ), | |
| 219 | + | ] { | |
| 104 | 220 | css.push('\n'); | |
| 105 | 221 | css.push_str(&narrowing_css( | |
| 106 | 222 | columns, |
| @@ -415,24 +415,25 @@ | |||
| 415 | 415 | <span class="past-events-count" id="recurring-events-count">0</span> | |
| 416 | 416 | </summary> | |
| 417 | 417 | <div class="event-table-virtual" id="recurring-event-table"> | |
| 418 | - | <div class="event-header-row" role="row"> | |
| 419 | - | <div class="event-cell" role="columnheader">Pattern</div> | |
| 420 | - | <div class="event-cell" role="columnheader">Time</div> | |
| 421 | - | <div class="event-cell" role="columnheader">Event</div> | |
| 422 | - | <div class="event-cell" role="columnheader">Location</div> | |
| 423 | - | <div class="event-cell" role="columnheader"></div> | |
| 418 | + | <div class="event-header-row event-header-recurring" role="row"> | |
| 419 | + | <div class="event-cell col-pattern" role="columnheader">Pattern</div> | |
| 420 | + | <div class="event-cell col-time" role="columnheader">Time</div> | |
| 421 | + | <div class="event-cell col-title" role="columnheader">Event</div> | |
| 422 | + | <div class="event-cell col-location" role="columnheader">Location</div> | |
| 423 | + | <div class="event-cell col-actions" role="columnheader"></div> | |
| 424 | 424 | </div> | |
| 425 | 425 | <div class="event-list-container" id="recurring-event-list-container"></div> | |
| 426 | 426 | </div> | |
| 427 | 427 | </details> | |
| 428 | 428 | <h3 class="events-section-heading hidden" id="future-events-heading">Upcoming</h3> | |
| 429 | 429 | <div class="event-table-virtual" id="event-table" role="grid" aria-label="Upcoming events"> | |
| 430 | - | <div class="event-header-row" role="row"> | |
| 431 | - | <div class="event-cell" role="columnheader">Date</div> | |
| 432 | - | <div class="event-cell" role="columnheader">Time</div> | |
| 433 | - | <div class="event-cell" role="columnheader">Event</div> | |
| 434 | - | <div class="event-cell" role="columnheader">Location</div> | |
| 435 | - | <div class="event-cell" role="columnheader"></div> | |
| 430 | + | <div class="event-header-row event-header-upcoming" role="row"> | |
| 431 | + | <div class="event-cell col-select" role="columnheader"><span class="sr-only">Select</span></div> | |
| 432 | + | <div class="event-cell col-date" role="columnheader">Date</div> | |
| 433 | + | <div class="event-cell col-time" role="columnheader">Time</div> | |
| 434 | + | <div class="event-cell col-title" role="columnheader">Event</div> | |
| 435 | + | <div class="event-cell col-location" role="columnheader">Location</div> | |
| 436 | + | <div class="event-cell col-actions" role="columnheader"></div> | |
| 436 | 437 | </div> | |
| 437 | 438 | <div class="event-list-container well" id="event-list-container" aria-live="polite"> | |
| 438 | 439 | <div class="skeleton-shimmer" aria-label="Loading events"> |
| @@ -1298,11 +1298,14 @@ | |||
| 1298 | 1298 | box-shadow: var(--bevel-raised); | |
| 1299 | 1299 | } | |
| 1300 | 1300 | ||
| 1301 | - | /* Event Grid Column Widths */ | |
| 1301 | + | /* Event Grid Column Widths | |
| 1302 | + | grid-template-columns comes from css/tables.css. There are two tables here, | |
| 1303 | + | not one: the upcoming list leads with a select checkbox and a date, the | |
| 1304 | + | recurring list with a pattern and no selection. They shared this rule until | |
| 1305 | + | 2026-07-30, which put the upcoming row's six cells into five tracks. */ | |
| 1302 | 1306 | .event-header-row, | |
| 1303 | 1307 | .event-row-virtual { | |
| 1304 | 1308 | display: grid; | |
| 1305 | - | grid-template-columns: 100px 80px 1fr 150px 40px; | |
| 1306 | 1309 | align-items: center; | |
| 1307 | 1310 | gap: var(--gap-group); | |
| 1308 | 1311 | } | |
| @@ -2687,11 +2690,6 @@ | |||
| 2687 | 2690 | .error-state--padded { | |
| 2688 | 2691 | padding: var(--gap-group); | |
| 2689 | 2692 | } | |
| 2690 | - | .event-cell--shrink { | |
| 2691 | - | flex: 0 0 auto; | |
| 2692 | - | padding-right: 0; | |
| 2693 | - | } | |
| 2694 | - | ||
| 2695 | 2693 | .hr-soft { | |
| 2696 | 2694 | border: none; | |
| 2697 | 2695 | border-top: var(--border-width) solid var(--border); |
| @@ -542,11 +542,11 @@ | |||
| 542 | 542 | data-act="events.open" data-a1="${escAttr(e.id)}" | |
| 543 | 543 | data-contextmenu="contextMenus.showEvent" data-a1="@event" data-a2="${escAttr(e.id)}" | |
| 544 | 544 | tabindex="0" role="row"> | |
| 545 | - | <div class="event-cell event-cell-date"><span class="event-recurrence-pattern">${esc(patternLabel)}</span></div> | |
| 546 | - | <div class="event-cell row-secondary event-cell-time">${e.timeFormatted}</div> | |
| 547 | - | <div class="event-cell event-cell-title">${esc(displayTitle)}</div> | |
| 548 | - | <div class="event-cell row-secondary event-cell-location">${e.location ? esc(e.location) : '-'}</div> | |
| 549 | - | <div class="event-cell" style="text-align: right;" data-act="ui.noop"> | |
| 545 | + | <div class="event-cell col-pattern event-cell-date"><span class="event-recurrence-pattern">${esc(patternLabel)}</span></div> | |
| 546 | + | <div class="event-cell col-time row-secondary event-cell-time">${e.timeFormatted}</div> | |
| 547 | + | <div class="event-cell col-title event-cell-title">${esc(displayTitle)}</div> | |
| 548 | + | <div class="event-cell col-location row-secondary event-cell-location">${e.location ? esc(e.location) : '-'}</div> | |
| 549 | + | <div class="event-cell col-actions" style="text-align: right;" data-act="ui.noop"> | |
| 550 | 550 | <button class="button--icon row-actions kebab-btn" data-act="contextMenus.showEvent" data-a1="@event" data-a2="${escAttr(e.id)}" title="Actions" aria-label="Event actions">⋮</button> | |
| 551 | 551 | </div> | |
| 552 | 552 | </div> | |
| @@ -573,24 +573,24 @@ | |||
| 573 | 573 | ||
| 574 | 574 | return ` | |
| 575 | 575 | ${dateHeader} | |
| 576 | - | <div class="row event-row-virtual ${e.isPast || isPast ? 'event-past' : ''}" | |
| 576 | + | <div class="row event-row-virtual event-upcoming ${e.isPast || isPast ? 'event-past' : ''}" | |
| 577 | 577 | data-id="${escAttr(e.id)}" | |
| 578 | 578 | data-act="events.open" data-a1="${escAttr(e.id)}" | |
| 579 | 579 | data-contextmenu="contextMenus.showEvent" data-a1="@event" data-a2="${escAttr(e.id)}" | |
| 580 | 580 | tabindex="0" role="row"> | |
| 581 | - | <div class="event-cell event-cell--shrink"> | |
| 581 | + | <div class="event-cell col-select"> | |
| 582 | 582 | <input type="checkbox" class="bulk-checkbox event-select-cb" data-id="${escAttr(e.id)}" | |
| 583 | 583 | data-act="events.toggleEventSelection" data-a1="${escAttr(e.id)}" data-a2="@event" | |
| 584 | 584 | aria-label="Select event"> | |
| 585 | 585 | </div> | |
| 586 | - | <div class="event-cell event-cell-date"> | |
| 586 | + | <div class="event-cell col-date event-cell-date"> | |
| 587 | 587 | <span class="row-primary event-date-num">${startDate.getDate()} ${monthName}</span> | |
| 588 | 588 | <span class="event-date-badge event-proximity-${e.proximityClass || 'default'}">${e.proximityLabel || ''}</span> | |
| 589 | 589 | </div> | |
| 590 | - | <div class="event-cell row-secondary event-cell-time">${e.timeFormatted}</div> | |
| 591 | - | <div class="event-cell event-cell-title">${esc(displayTitle)}</div> | |
| 592 | - | <div class="event-cell row-secondary event-cell-location">${e.location ? esc(e.location) : '-'}</div> | |
| 593 | - | <div class="event-cell" style="text-align: right;" data-act="ui.noop"> | |
| 590 | + | <div class="event-cell col-time row-secondary event-cell-time">${e.timeFormatted}</div> | |
| 591 | + | <div class="event-cell col-title event-cell-title">${esc(displayTitle)}</div> | |
| 592 | + | <div class="event-cell col-location row-secondary event-cell-location">${e.location ? esc(e.location) : '-'}</div> | |
| 593 | + | <div class="event-cell col-actions" style="text-align: right;" data-act="ui.noop"> | |
| 594 | 594 | <button class="button--icon row-actions kebab-btn" data-act="contextMenus.showEvent" data-a1="@event" data-a2="${escAttr(e.id)}" title="Actions" aria-label="Event actions">⋮</button> | |
| 595 | 595 | </div> | |
| 596 | 596 | </div> |