oficina / sheets Public

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

Plain git — no account needed to clone.

Download

Download this file

1/* Fit columns to displayed values, using each cell's actual font and padding.
2 * Raw formulas are deliberately not measured: their displayed results are. */
3(function () {
4 'use strict';
5 var host = document.querySelector('[data-sheet-grid]');
6 if (!host) return;
7 var canvas = document.createElement('canvas');
8 var context = canvas.getContext('2d');
9 var fitting = false;
10
11 function width(el) {
12 if (!el || !el.textContent) return 0;
13 var style = getComputedStyle(el);
14 context.font = style.font || [style.fontStyle, style.fontWeight, style.fontSize, style.fontFamily].join(' ');
15 var spacing = parseFloat(style.letterSpacing) || 0;
16 var widest = 0;
17 el.textContent.split(/\r?\n/).forEach(function (line) {
18 widest = Math.max(widest, context.measureText(line).width + Math.max(0, line.length - 1) * spacing);
19 });
20 return widest + (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0) +
21 (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.borderRightWidth) || 0) + 4;
22 }
23
24 window.SheetsAutoFit = async function (column) {
25 var g = window.SheetsGrid;
26 if (!g || !g.mayWrite || fitting || !context) return;
27 var ws = g.worksheet(), s = g.selection();
28 if (!ws || (!s && typeof column !== 'number')) return;
29 var first = s ? Math.min(s.c, s.c2) : column;
30 var last = s ? Math.max(s.c, s.c2) : column;
31 // A divider outside the selection fits just its own column.
32 if (typeof column === 'number' && (column < first || column > last)) first = last = column;
33 fitting = true;
34 try {
35 if (document.fonts) await document.fonts.ready;
36 if (window.SheetsSizes) await window.SheetsSizes.settled();
37 for (var c = first; c <= last; c++) {
38 var size = Math.max(16, width(ws.headers[c]));
39 for (var r = 0; r < g.rows; r++) {
40 var td = ws.records[r] && ws.records[r][c] && ws.records[r][c].element;
41 size = Math.max(size, width(td));
42 }
43 // The server's existing width bounds also govern auto-fit.
44 if (!await g.post('/size', { col: c, size: Math.min(2000, Math.ceil(size)) })) break;
45 }
46 var surface = host.closest('[data-sheet-surface]');
47 if (surface.dataset.windowTruncated && window.SheetsNote) {
48 window.SheetsNote.say('Columns fitted to the loaded cells; this sheet extends beyond the grid window.', 'warn');
49 }
50 } finally { fitting = false; }
51 };
52
53 // The vendor treats even an unmoved resize click as a group resize. Suppress
54 // that no-op so the two clicks before auto-fit don't first equalize widths.
55 document.addEventListener('mouseup', function () {
56 var g = window.SheetsGrid, ws = g && g.worksheet();
57 var resize = ws && ws.resizing;
58 if (!resize || resize.column === undefined) return;
59 var col = ws.cols[resize.column];
60 if (!col || Number(col.colElement.getAttribute('width')) !== Number(resize.width)) return;
61 host.querySelectorAll('.resizing').forEach(function (el) { el.classList.remove('resizing'); });
62 ws.resizing = null;
63 }, true);
64
65 host.addEventListener('dblclick', function (e) {
66 var td = e.target.closest('thead td[data-x]');
67 if (!td) return;
68 var rect = td.getBoundingClientRect();
69 var col = Number(td.dataset.x);
70 // Match the vendor's resize handle so mousedown preserves the selection.
71 if (rect.right - e.clientX > 6) return;
72 e.preventDefault();
73 e.stopImmediatePropagation();
74 window.SheetsAutoFit(col);
75 }, true);
76}());
77