| 1 | /* The status line under the grid. |
| 2 | * |
| 3 | * One sentence, cleared after a few seconds. It is the only way this app |
| 4 | * can tell somebody that a thing they asked for did not happen, and |
| 5 | * before it existed there was no such way at all: a browser refusing the |
| 6 | * menu's clipboard read, or a sheet reaching past the widest grid this |
| 7 | * build can paint, both simply went unsaid. |
| 8 | * |
| 9 | * Its own module rather than a function inside whichever module first |
| 10 | * needed it. Two already do — clipboard.js and grid.js — and a shared |
| 11 | * thing living inside one caller is how the second caller ends up with a |
| 12 | * second copy that clears the first one's timer. |
| 13 | * |
| 14 | * role="status" is on the element in sheet.html and is right there for a |
| 15 | * reason worth writing down, because the element beside it deliberately |
| 16 | * does NOT have it: the selection size changes on every arrow key and a |
| 17 | * live region announcing it would talk over the cell a screen reader is |
| 18 | * reading. This changes only when something happened, which is exactly |
| 19 | * what should be read out. |
| 20 | */ |
| 21 | (function () { |
| 22 | 'use strict'; |
| 23 | |
| 24 | var el = document.querySelector('[data-sheet-note]'); |
| 25 | if (!el) return; |
| 26 | |
| 27 | var timer = null; |
| 28 | |
| 29 | /* The one thing this module says on its own behalf. |
| 30 | * |
| 31 | * The server sets data-window-truncated when the sheet reaches past |
| 32 | * the widest grid this build can paint. |
| 33 | * |
| 34 | * The wording is exact, and the first version was WRONG: it said "the |
| 35 | * table view lists every row". The table view paginates |
| 36 | * maxWindowRows/tableRowsPerPage — twenty pages — and its write route |
| 37 | * refuses a row at or past maxWindowRows, so past that ceiling the |
| 38 | * data is in NEITHER editing surface. Sending somebody to a screen |
| 39 | * that cannot show them the thing is worse than the silence this |
| 40 | * notice replaced, because it costs them the trip. The export is the |
| 41 | * one path that genuinely carries it. It is said here rather than |
| 42 | * rendered as a banner because a banner over a grid is permanent and |
| 43 | * this is not news twice; and it is read from the surface's own |
| 44 | * dataset rather than from window.SheetsGrid so that a sheet whose |
| 45 | * grid failed to boot still tells you its rows exist somewhere. |
| 46 | * |
| 47 | * Deferred to the next task so the status element is not written |
| 48 | * before role="status" has been observed — a live region populated in |
| 49 | * the same tick it is parsed is not reliably announced. */ |
| 50 | var surface = document.querySelector('[data-sheet-surface]'); |
| 51 | if (surface && surface.dataset.windowTruncated) { |
| 52 | setTimeout(function () { |
| 53 | window.SheetsNote.say('This sheet reaches past the ' + |
| 54 | surface.dataset.rows + ' rows and ' + surface.dataset.cols + |
| 55 | ' columns any screen here can show. Everything is in the .xlsx ' + |
| 56 | 'download.'); |
| 57 | }, 0); |
| 58 | } |
| 59 | |
| 60 | window.SheetsNote = { |
| 61 | /* say replaces whatever is showing. It does not queue: two things |
| 62 | * worth saying at once is a design problem, and a queue would show |
| 63 | * the second one to somebody who has already moved on. */ |
| 64 | say: function (text, tone) { |
| 65 | el.textContent = text; |
| 66 | /* A message the person has to ACT on looks different from one |
| 67 | * that is merely informative. Without this the note was muted |
| 68 | * grey in the strip under the grid, which is where nobody looks |
| 69 | * — and "the menu could not paste, press Ctrl+V" went unread |
| 70 | * while the menu item appeared simply broken. Reported against |
| 71 | * the deployed canary, 2026-09-04. */ |
| 72 | if (tone) el.setAttribute('data-tone', tone); |
| 73 | else el.removeAttribute('data-tone'); |
| 74 | if (timer) clearTimeout(timer); |
| 75 | timer = setTimeout(function () { |
| 76 | el.textContent = ''; |
| 77 | el.removeAttribute('data-tone'); |
| 78 | }, 12000); |
| 79 | } |
| 80 | }; |
| 81 | }()); |
| 82 | |