oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

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