// add-row.js — inline new-row creation.
//
// Click "+ Add row" → append a draft row at the end of state.rows,
// focus its first editable cell, accumulate user typing into the
// drafts buffer like any other row. On row-blur, save.js detects the
// row.isNew flag and POSTs to
/form.html (the form-create
// endpoint). The 201 response carries the new row's Location; we swap
// the synthetic url/yamlUrl for the real ones and the draft row
// becomes a normal saved row.
//
// Synthetic identity: each new row gets a temporary "__new-" url
// so rowKey() returns something unique for selection + draft tracking.
// The temporary url is replaced after a successful POST. There is no
// "save on click" UX — the existing row-blur trigger is the save path,
// same as for edits.
(function (app) {
'use strict';
let _counter = 0;
function makeSyntheticKey() {
_counter += 1;
return '__new-' + _counter;
}
// Compute the form-create URL for the current page. Both
// //table.html and // (default_tool: tables) shape work;
// //form.html is the form handler's "create" endpoint either
// way (the form handler keys off the in-dir convention, not the
// visiting URL shape).
function formCreateUrl() {
let dir = (location.pathname || '/').replace(/\/table\.html$/, '/');
if (!dir.endsWith('/')) dir += '/';
return dir + 'form.html';
}
// Create-and-paint: the user-facing path.
function invoke() {
const key = createSilent();
if (typeof app.repaint === 'function') app.repaint();
focusNewRow(key);
}
// Push a draft row WITHOUT painting or focusing. Used by multi-row
// paste (clipboard.js) to create N rows in a single batch, with one
// paint at the end. Returns the synthetic url so callers can address
// the new row in their draft writes.
function createSilent() {
const key = makeSyntheticKey();
const draftRow = {
url: key,
yamlUrl: null,
data: {},
etag: null,
editable: true,
isNew: true,
};
if (!Array.isArray(app.state.rows)) {
app.state.rows = [];
}
app.state.rows.push(draftRow);
return key;
}
function focusNewRow(key) {
// After repaint, find the tr with our synthetic data-row-id and
// tell the editor to select its first cell. Filtering may have
// hidden the new row if a default filter excludes it; we accept
// that — clearing filters surfaces it.
const tbody = document.querySelector('#table-root tbody');
if (!tbody) return;
const trs = tbody.querySelectorAll('tr');
for (let i = 0; i < trs.length; i++) {
if (trs[i].getAttribute('data-row-id') === key) {
const editor = app.modules.editor;
if (editor && typeof editor.setSelected === 'function') {
// Scroll into view so the user sees the new row.
trs[i].scrollIntoView({ block: 'nearest', behavior: 'auto' });
editor.setSelected(i, 0);
}
return;
}
}
}
// Cancel-new-row helper: drop the synthetic row entirely. Used when
// the user adds a row, makes no edits, and clicks Add again or
// navigates away — there's nothing to save and an empty draft just
// clutters the table. The save module calls this from row-blur when
// it sees a new row with no drafts.
function discardEmpty(rowId) {
const rows = app.state.rows || [];
for (let i = 0; i < rows.length; i++) {
if (rows[i].isNew && rows[i].url === rowId) {
rows.splice(i, 1);
if (typeof app.repaint === 'function') app.repaint();
return true;
}
}
return false;
}
app.modules.addRow = {
invoke: invoke,
createSilent: createSilent,
formCreateUrl: formCreateUrl,
discardEmpty: discardEmpty,
};
})(window.tablesApp);