oficina / sheets Public

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

Plain git — no account needed to clone.

Download

Download this file

1/* Point at cells while entering a formula, without committing the edited cell.
2 * This inserts references only; calculation remains on the server. */
3(function () {
4 'use strict';
5 var host = document.querySelector('[data-sheet-grid]');
6 if (!host) return;
7 var drag = null;
8
9 function editor() {
10 var el = document.activeElement;
11 if (!el || !el.matches('input, textarea') || el.readOnly) return null;
12 if (!el.matches('[data-formula-input]') && !host.contains(el)) return null;
13 if (el.value.charAt(0) !== '=' || el.selectionStart === null) return null;
14 return el;
15 }
16 function cell(target) {
17 var td = target.closest && target.closest('td[data-x][data-y]');
18 return td && host.contains(td) ? td : null;
19 }
20 function ref(c, r) {
21 var name = '';
22 for (c++; c; c = Math.floor((c - 1) / 26)) {
23 name = String.fromCharCode(65 + (c - 1) % 26) + name;
24 }
25 return name + (r + 1);
26 }
27 function clear() {
28 host.querySelectorAll('[data-formula-reference]').forEach(function (td) {
29 td.removeAttribute('data-formula-reference');
30 });
31 }
32 function point(td) {
33 var c = Number(td.dataset.x), r = Number(td.dataset.y);
34 var c1 = Math.min(c, drag.c), r1 = Math.min(r, drag.r);
35 var c2 = Math.max(c, drag.c), r2 = Math.max(r, drag.r);
36 var text = ref(c1, r1);
37 if (c1 !== c2 || r1 !== r2) text += ':' + ref(c2, r2);
38 drag.el.value = drag.before + text + drag.after;
39 drag.end = drag.before.length + text.length;
40 drag.el.setSelectionRange(drag.end, drag.end);
41 drag.el.dispatchEvent(new Event('input', { bubbles: true }));
42 clear();
43 host.querySelectorAll('td[data-x][data-y]').forEach(function (item) {
44 if (+item.dataset.x >= c1 && +item.dataset.x <= c2 &&
45 +item.dataset.y >= r1 && +item.dataset.y <= r2) {
46 item.setAttribute('data-formula-reference', '');
47 }
48 });
49 }
50 document.addEventListener('mousedown', function (e) {
51 if (e.button !== 0) return;
52 var el = editor(), td = cell(e.target);
53 if (!el || !td || td.contains(el)) return;
54 var start = el.selectionStart, end = el.selectionEnd;
55 if (start < 1) return;
56 // Point mode begins where an operand belongs, or replaces selected text.
57 // Else leave ordinary clicks alone so a completed formula can be committed.
58 if (start === end && !/[=+\-*/^(,:<>;&]\s*$/.test(el.value.slice(0, start))) return;
59 e.preventDefault();
60 e.stopImmediatePropagation();
61 drag = { el: el, before: el.value.slice(0, start), after: el.value.slice(end),
62 c: Number(td.dataset.x), r: Number(td.dataset.y) };
63 point(td);
64 }, true);
65 document.addEventListener('mousemove', function (e) {
66 if (!drag) return;
67 e.preventDefault();
68 e.stopImmediatePropagation();
69 var td = cell(e.target);
70 if (td) point(td);
71 }, true);
72 document.addEventListener('mouseup', function (e) {
73 if (!drag) return;
74 e.preventDefault();
75 e.stopImmediatePropagation();
76 drag.el.focus();
77 drag.el.setSelectionRange(drag.end, drag.end);
78 drag = null;
79 }, true);
80 document.addEventListener('keydown', function () { if (!drag) clear(); }, true);
81 document.addEventListener('focusout', function () { if (!drag) clear(); });
82 window.addEventListener('blur', function () { drag = null; clear(); });
83}());
84