oficina / sheets Public

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

Plain git — no account needed to clone.

Download

Download this file

1/* Column widths and row heights.
2 *
3 * Its own module for the same reason paint.js is: grid.js owns the
4 * library's lifecycle and the selection, and everything else that needs
5 * a hook there should arrive as one. It also keeps grid.js under the
6 * 300-line cap without any of the three files having to be terse.
7 *
8 * extend() is the seam. grid.js hands it the options object on its way
9 * into jspreadsheet(), because the resize callbacks are SPREADSHEET-
10 * level options — nested a level deeper they are accepted in silence
11 * and never called, which is a mistake this app has already made once
12 * with onchange and which cost two real browser tabs to find.
13 */
14(function () {
15 'use strict';
16
17 /* True while we are applying sizes the server sent. The library fires
18 * its resize callbacks for a programmatic setWidth just as it does
19 * for a drag, so without this every incoming size is echoed straight
20 * back — two tabs open, and one person's drag becomes an endless
21 * round trip between them. */
22 var applying = false;
23 var pending = Promise.resolve();
24
25 function apply(ws, sizes) {
26 if (!ws || !sizes) return;
27 applying = true;
28 try {
29 each(sizes.cols, function (i, px) { if (ws.setWidth) ws.setWidth(i, px); });
30 each(sizes.rows, function (i, px) { if (ws.setHeight) ws.setHeight(i, px); });
31 } finally {
32 applying = false;
33 }
34 }
35
36 function each(map, fn) {
37 if (!map) return;
38 Object.keys(map).forEach(function (k) {
39 var i = parseInt(k, 10);
40 var px = map[k];
41 if (isFinite(i) && i >= 0 && typeof px === 'number' && px > 0) fn(i, px);
42 });
43 }
44
45 /* extend adds the two resize callbacks to the options grid.js is
46 * about to construct the sheet with.
47 *
48 * ctx carries what this module cannot see: where to post, who we are,
49 * whether this viewer may write, and how to paint a response. */
50 function extend(opts, ctx) {
51 if (!ctx.mayWrite) return opts;
52
53 opts.onresizecolumn = function (instance, col, width) {
54 // A multi-column drag reports an array; persist every selected column.
55 (Array.isArray(col) ? col : [col]).forEach(function (index) {
56 post(ctx, { col: parseInt(index, 10), size: Math.round(width) });
57 });
58 };
59 opts.onresizerow = function (instance, row, height) {
60 post(ctx, { row: parseInt(row, 10), size: Math.round(height) });
61 };
62 return opts;
63 }
64
65 function post(ctx, body) {
66 if (applying) return;
67 if (!isFinite(body.size) || body.size <= 0) return;
68 body.client = ctx.client;
69 pending = pending.then(function () {
70 return fetch(ctx.base + '/size', {
71 method: 'POST',
72 credentials: 'same-origin',
73 headers: { 'Content-Type': 'application/json' },
74 body: JSON.stringify(body)
75 }).then(function (resp) {
76 /* A refused resize is not repainted from the response — the
77 * column is already the width the person dragged it to, and
78 * snapping it back under their cursor is worse than leaving a
79 * width the server did not keep. The next reload is the
80 * correction, and a refusal here means a bound was hit, which is
81 * visible in the drag itself. */
82 if (!resp.ok) return;
83 return resp.json().then(function (payload) { ctx.paint(payload); });
84 }).catch(function () { /* the stream's own reconnect will resync */ });
85 });
86 }
87
88 window.SheetsSizes = { apply: apply, extend: extend, settled: function () { return pending; } };
89}());
90