|
| 1 | +# Ranklist Render Options Props |
| 2 | + |
| 3 | +This document is the agent-facing source of truth for the optional render props |
| 4 | +being piloted in the React package before promotion to the other framework |
| 5 | +packages. |
| 6 | + |
| 7 | +## Execution Constraints |
| 8 | + |
| 9 | +- Work happens on branch `feat/ranklist-render-options`. |
| 10 | +- Do not use a git worktree for this feature. |
| 11 | +- Do not create commits unless the user explicitly asks for a commit. |
| 12 | +- Preserve unrelated local changes. |
| 13 | + |
| 14 | +## React Pilot API |
| 15 | + |
| 16 | +Add the following optional props to React `RanklistProps`: |
| 17 | + |
| 18 | +```ts |
| 19 | +splitOrganization?: boolean; |
| 20 | +columnTitles?: RanklistColumnTitles; |
| 21 | +statusCellPreset?: 'classic' | 'detailed' | 'minimal' | 'compact'; |
| 22 | +statusColorAsText?: boolean; |
| 23 | +showProblemStatisticsFooter?: boolean; |
| 24 | +showDirtColumn?: boolean; |
| 25 | +showSEColumn?: boolean; |
| 26 | +rowBordered?: boolean; |
| 27 | +columnBordered?: boolean; |
| 28 | +emptyStatusPlaceholder?: string | null; |
| 29 | +userAvatarPlacement?: 'user' | 'organization'; |
| 30 | +``` |
| 31 | + |
| 32 | +`RanklistColumnTitles` is text-only so the same concept can later map cleanly to |
| 33 | +Vue, Solid, Svelte, and Angular: |
| 34 | + |
| 35 | +```ts |
| 36 | +interface RanklistColumnTitles { |
| 37 | + series?: string[] | ((series: srk.RankSeries, index: number) => string | undefined); |
| 38 | + organization?: string; |
| 39 | + user?: string; |
| 40 | + score?: string; |
| 41 | + time?: string; |
| 42 | + dirt?: string; |
| 43 | + se?: string; |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +Default labels are: series title from SRK, `Name`, `Organization`, `Score`, |
| 48 | +`Time`, `Dirt`, and `SE`. |
| 49 | + |
| 50 | +## Prop Reference |
| 51 | + |
| 52 | +| Prop | Default | Contract | |
| 53 | +| --- | --- | --- | |
| 54 | +| `splitOrganization` | `false` | Inserts an Organization column before User and hides organization text from the default User cell. | |
| 55 | +| `columnTitles` | SRK/default labels | Text-only title overrides for series, organization, user, score, time, dirt, and se columns. | |
| 56 | +| `statusCellPreset` | `classic` | Chooses the reusable status content preset: `classic`, `detailed`, `minimal`, or `compact`. | |
| 57 | +| `statusColorAsText` | `false` | Removes status fill backgrounds and uses bold status-colored text; FB adds a gold star marker. | |
| 58 | +| `showProblemStatisticsFooter` | `false` | Renders the multi-row problem statistics footer plus the final problem alias row. | |
| 59 | +| `showDirtColumn` | `false` | Appends the row Dirt percentage column after problem columns. | |
| 60 | +| `showSEColumn` | `false` | Appends the row SE column after problems and after Dirt when both are enabled. | |
| 61 | +| `rowBordered` | `false` | Enables row separators via shared CSS variables. Existing `borderedRows` remains a React alias. | |
| 62 | +| `columnBordered` | `false` | Enables column separators via shared CSS variables. | |
| 63 | +| `emptyStatusPlaceholder` | `null` | Replaces no-submission status cell blank content with a custom string. | |
| 64 | +| `userAvatarPlacement` | `user` | Moves the default avatar into the split Organization column only when set to `organization` and `splitOrganization` is enabled. | |
| 65 | + |
| 66 | +## Behavior Rules |
| 67 | + |
| 68 | +- `splitOrganization` inserts an Organization column between series columns and |
| 69 | + User. The default user cell must not duplicate organization text when the |
| 70 | + split column is active. |
| 71 | +- `userAvatarPlacement` defaults to `user`. `organization` only has an effect |
| 72 | + when `splitOrganization` is enabled; in that case the default avatar moves |
| 73 | + from the User column into the Organization column before the organization |
| 74 | + text. Without split organization, avatars stay in the User column. |
| 75 | +- `statusCellPreset` values: |
| 76 | + - `classic`: current rendering. |
| 77 | + - `detailed`: accepted line 1 is pass time, line 2 is `(-n)` when wrong tries |
| 78 | + exist. Rejected/frozen line 1 is an empty placeholder, line 2 is `(-tries)` |
| 79 | + when tries exist. |
| 80 | + - `minimal`: accepted is `+` or `+n`; rejected/frozen is `-n`. |
| 81 | + - `compact`: minimal first line plus pass time on a second line for accepted |
| 82 | + cells. Rejected/frozen cells render `-n`; when `tries > 0` and `solutions` |
| 83 | + contains at least one penalty-bearing solution after filtering |
| 84 | + `sorter.config.noPenaltyResults`, render the last such solution time on a |
| 85 | + second line. If no penalty-bearing solution exists, keep the single `-n` |
| 86 | + line. |
| 87 | +- No-submission statuses remain blank in all presets. |
| 88 | +- `emptyStatusPlaceholder` changes only no-submission status cells (`result: |
| 89 | + null`). Its default is `null`, which preserves the blank cell behavior. |
| 90 | +- Status time formatting uses ICPC sorter `config.timePrecision` when present, |
| 91 | + otherwise the status time unit. Minute or coarser displays `h:mm`, seconds |
| 92 | + displays `h:mm:ss`, milliseconds displays `h:mm:ss.SSS`. |
| 93 | +- `statusColorAsText` removes status fill backgrounds and uses bold colored |
| 94 | + text for status emphasis. FB cells additionally render a gold star inside the |
| 95 | + cell near the top-right corner. |
| 96 | +- Footer statistics use `status.tries` as the valid submission source: |
| 97 | + - Accepted: number of users whose status result is `AC` or `FB`. |
| 98 | + The cell shows a second line `(r%)`, where `r` is |
| 99 | + `Math.floor(accepted / participantCount * 100)`. If the participant count |
| 100 | + is `0`, show `(-)`. |
| 101 | + - Tried: number of users with `tries > 0`. |
| 102 | + The cell shows a second line `(r%)`, where `r` is |
| 103 | + `Math.floor(tried / participantCount * 100)`. If the participant count is |
| 104 | + `0`, show `(-)`. |
| 105 | + - Submitted: sum of `tries`. |
| 106 | + - Dirt: among users who accepted this problem (`AC` or `FB`), sum |
| 107 | + `max(tries - 1, 0)` as the first line. The second line is `(r%)`, where |
| 108 | + `r` is `Math.floor(dirt / acceptedSubmitted * 100)` and |
| 109 | + `acceptedSubmitted` is the sum of `tries` for accepted users. If |
| 110 | + `acceptedSubmitted` is `0`, show `(-)`. |
| 111 | + - SE: average hardness, formatted with two decimals as |
| 112 | + `(participantCount - accepted) / participantCount` using round-to-nearest |
| 113 | + formatting. If the participant count is `0`, show `-`. |
| 114 | + - FB at and LB at are separate rows, formatted as floored minute integers. |
| 115 | +- Footer label rows use the shared marker tooltip style with these English |
| 116 | + explanations. The tooltip class must be attached to the inner label text |
| 117 | + element, not the full footer row, so the tooltip is anchored on the text |
| 118 | + itself. Footer statistic tooltips are positioned to the left of their labels |
| 119 | + and the tooltip pseudo-element must not receive pointer events, otherwise the |
| 120 | + hover hot zone can drift to the tooltip bubble: |
| 121 | + - Accepted: number of participants who solved this problem |
| 122 | + - Tried: number of participants who attempted this problem |
| 123 | + - Submitted: total number of valid submissions for this problem |
| 124 | + - Dirt: wrong submissions among participants who solved this problem |
| 125 | + - SE: average hardness, calculated as `(participants - accepted) / participants` |
| 126 | + - FB at: First Blood at, also known as first solve time, in minutes |
| 127 | + - LB at: Last Blood at, also known as last solve time, in minutes |
| 128 | +- Footer statistics must be structured as real table rows: one `<tr>` per |
| 129 | + statistic item, with the left label cell spanning all non-problem columns and |
| 130 | + each problem value in its own cell. Do not stack all statistic rows inside a |
| 131 | + single footer cell; that breaks label/value alignment and prevents footer |
| 132 | + rows from naturally following row striping and row border styles. Row striping |
| 133 | + and row borders apply only to per-problem statistic value cells; do not apply |
| 134 | + them to the left label cell or to right-side alignment-only extra cells such |
| 135 | + as the Dirt or SE footer cells. |
| 136 | +- Footer statistics end with one extra problem label row after `LB at`. Its |
| 137 | + per-problem cells render only the problem alias (or the alphabet fallback) |
| 138 | + and reuse the problem-header background treatment with the gradient direction |
| 139 | + reversed by 180 degrees, without the header's accepted-count second line. |
| 140 | + Their background is clipped to the padding box so transparent row-border |
| 141 | + space does not show a colored top edge when row borders are disabled. |
| 142 | +- Dirt only considers accepted problems. Numerator is sum of `tries - 1`; |
| 143 | + denominator is sum of `tries`; percentages are floored integers and zero |
| 144 | + denominator renders `0%`. |
| 145 | +- `showSEColumn` appends a contestant `SE` column after the problem columns. |
| 146 | + When `showDirtColumn` is also enabled, `SE` is placed after `Dirt`. |
| 147 | + Contestant SE is the average of the per-problem SE values for every problem |
| 148 | + the row accepted (`AC` or `FB`). A row with no accepted problems renders |
| 149 | + `0.00`. Contestant SE uses the same two-decimal round-to-nearest formatting |
| 150 | + as the footer SE row. |
| 151 | +- `Score`, `Time`, `Dirt`, and `SE` column headers are right-aligned to match |
| 152 | + their numeric body cells. |
| 153 | +- If the footer and appended extra columns are both enabled, the footer gets |
| 154 | + one empty alignment cell for each enabled extra column, in column order |
| 155 | + (`Dirt`, then `SE`). |
| 156 | +- `rowBordered` enables a light horizontal separator between body rows. |
| 157 | + `borderedRows` remains supported as the existing React alias. The color is |
| 158 | + controlled by `--srk-table-row-border-color`, which defaults to |
| 159 | + `--srk-table-border` only when row borders are enabled. |
| 160 | +- `columnBordered` enables light vertical separators between columns. The color |
| 161 | + is controlled by `--srk-table-column-border-color`, which defaults to |
| 162 | + `--srk-table-border` only when column borders are enabled. Column separator |
| 163 | + border rules must be scoped behind `.srk-table-column-bordered`; transparent |
| 164 | + borders must not be emitted for the disabled state. Use an inset separator |
| 165 | + rather than collapsed table borders so sticky problem headers and body cells |
| 166 | + render the same separator color. Footer alignment-only extra cells for |
| 167 | + appended columns (`Dirt`, `SE`) must not render column separators. |
| 168 | +- Series segment markers are visual-only overlays, not real table borders. |
| 169 | + They must use an inner pseudo-element driven by |
| 170 | + `--srk-series-segment-border-width` and `--srk-series-segment-color`, so |
| 171 | + `columnBordered` separators stay aligned with headers and non-segment rows. |
| 172 | + Any series column that can display a preset segment marker must reserve |
| 173 | + right-side content padding for the whole column with |
| 174 | + `srk-series-segmented-column` and `--srk-series-segment-content-gap`; |
| 175 | + otherwise rows without an active marker no longer align with rows that do |
| 176 | + have one. When row borders are disabled, preset segment marker pseudo-elements |
| 177 | + also bleed across the transparent 1px row boundary with |
| 178 | + `--srk-series-segment-row-bleed`; `.srk-table-row-bordered` resets this bleed |
| 179 | + to `0px` so real row separators remain visible. |
| 180 | +- Problem header cells keep an opaque base background under their existing |
| 181 | + problem-style gradient and sit above body rows while sticky, so status text |
| 182 | + cannot show through during scroll. |
| 183 | +- Empty status placeholders use a centered placeholder cell class and must not |
| 184 | + inherit any accepted/failed/frozen/FB status highlight class. |
| 185 | + |
| 186 | +## Rollout Status |
| 187 | + |
| 188 | +- React: implemented in source; focused option tests, `pnpm test:react`, |
| 189 | + `pnpm build:styles`, `pnpm build:core`, and `pnpm build:react` have been |
| 190 | + run during the pilot. |
| 191 | + Latest feedback pass adds footer tooltip labels, wider footer row spacing, |
| 192 | + row/column border props, empty status placeholders, text-anchored footer |
| 193 | + tooltips, scoped column border CSS, and sticky problem header bleed-through |
| 194 | + protection. Follow-up QA changed column separators to inset shadows and moved |
| 195 | + footer statistic tooltips to the left side with pointer-events disabled. |
| 196 | + Latest compact preset update adds rejected/frozen second-line penalty solution |
| 197 | + time when `solutions` data can identify a last effective wrong submission. |
| 198 | + Latest avatar placement update adds `userAvatarPlacement` for moving the |
| 199 | + default avatar into the split Organization column. Latest series segment pass |
| 200 | + keeps marker bars out of table border geometry so column separators align |
| 201 | + when `columnBordered` is enabled, while preserving the old text-to-marker |
| 202 | + spacing across the whole affected series column and preventing row-gap breaks |
| 203 | + when row borders are disabled. Latest footer statistics update adds Accepted |
| 204 | + and Tried percentages, per-problem Dirt and SE rows, and renames FB/LB labels |
| 205 | + to `FB at` / `LB at`. Latest footer layout pass renders each statistic as its |
| 206 | + own table row so label/value alignment is handled by the table layout itself, |
| 207 | + and scopes footer striping/borders to problem statistic value cells only. |
| 208 | + Latest SE column update adds `showSEColumn`, keeps appended extra columns in |
| 209 | + `Dirt` then `SE` order, and standardizes SE formatting to two-decimal |
| 210 | + round-to-nearest output for both footer and contestant values. Final React |
| 211 | + pilot cleanup extracts status preset presentation into core and documents the |
| 212 | + prop contracts, shared implementation map, and test coverage matrix for the |
| 213 | + upcoming framework ports. |
| 214 | +- Vue, Solid, Svelte, Angular: pending React manual confirmation. |
| 215 | + |
| 216 | +## React Implementation Notes |
| 217 | + |
| 218 | +- Runtime helpers for shared logic live in core so later framework ports can use |
| 219 | + the same time formatting, status preset presentation, footer statistics, |
| 220 | + problem header background gradients, Dirt, and SE calculation rules. |
| 221 | +- Shared CSS for row/column borders, status color-as-text, FB star markers, |
| 222 | + split Organization/avatar layout, series segment bars, footer rows, footer |
| 223 | + tooltips, and footer problem alias cells lives in the styles package. |
| 224 | +- Framework packages should keep only framework rendering/composition local. |
| 225 | + Any portable value calculation or formatting needed by multiple renderers |
| 226 | + should be added to core before ports proceed. |
| 227 | +- The root Vitest workspace aliases the core package to `packages/core/src` so |
| 228 | + tests exercise source changes instead of stale local `dist` artifacts. |
| 229 | +- React still imports core by package name for normal builds; build order remains |
| 230 | + core before React. |
| 231 | + |
| 232 | +## Test Coverage |
| 233 | + |
| 234 | +- React option tests cover split organization, custom titles, right-aligned |
| 235 | + numeric/extra headers, avatar placement, all status presets, sorter precision |
| 236 | + time formatting, compact rejected penalty solution time, color-as-text FB star, |
| 237 | + footer statistics rows, footer tooltips, footer problem alias row, Dirt, SE, |
| 238 | + empty placeholders, and row/column border classes. |
| 239 | +- Structure tests cover shared CSS selector contracts for gated column borders, |
| 240 | + footer extra-cell border exclusions, opaque problem headers, footer problem |
| 241 | + alias row styling, left-positioned tooltips, footer striping/row borders, and |
| 242 | + segment marker geometry. |
| 243 | +- React dev tests cover the local `dev:react` controls for the new props, |
| 244 | + Showcase/Baseline presets, and the default modal wiring remains intact. |
0 commit comments