tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

arguments-ui.js (4252B)


      1 import { literals } from './codecs.js';
      2 import { parseLiteral, printLiteral } from './literal-input.js';
      3 export function argumentEditor(raw) {
      4     const container = document.getElementById('argument-fields');
      5     const add = document.getElementById('add-argument');
      6     const rawMode = document.getElementById('raw-arguments');
      7     let snapshot = '', disabled = false;
      8     let labels = [];
      9     let rows = [];
     10     function render() {
     11         container.replaceChildren();
     12         if (!rows.length) {
     13             const hint = document.createElement('p');
     14             hint.className = 'hint';
     15             hint.textContent = 'No inputs supplied. Add an input to apply this definition.';
     16             container.append(hint);
     17         }
     18         rows.forEach((row, i) => {
     19             const wrapper = document.createElement('div');
     20             wrapper.className = 'argument-row';
     21             const label = document.createElement('label');
     22             label.htmlFor = `argument-${i}`;
     23             label.textContent = labels[i] ?? `Argument ${i + 1}`;
     24             row.input.id = label.htmlFor;
     25             row.input.rows = 2;
     26             row.input.maxLength = 16000;
     27             row.input.spellcheck = false;
     28             row.input.disabled = disabled;
     29             const remove = document.createElement('button');
     30             remove.type = 'button';
     31             remove.textContent = 'Remove';
     32             remove.disabled = disabled;
     33             remove.setAttribute('aria-label', `Remove argument ${i + 1}`);
     34             remove.onclick = () => { rows.splice(i, 1); render(); sync(); };
     35             row.input.oninput = sync;
     36             wrapper.append(label, row.input, remove);
     37             container.append(wrapper);
     38         });
     39         container.hidden = rawMode.checked;
     40         add.hidden = rawMode.checked;
     41         add.disabled = disabled || rows.length >= 16;
     42         raw.hidden = !rawMode.checked;
     43         rawMode.disabled = disabled;
     44     }
     45     function values() {
     46         return literals(rows.map(row => row.original && row.input.value === row.text ? row.original : parseLiteral(row.input.value)));
     47     }
     48     function sync() {
     49         try {
     50             raw.value = JSON.stringify(values());
     51             snapshot = raw.value;
     52         }
     53         catch { /* Retain invalid field text for correction; read reports it. */ }
     54     }
     55     function refresh(reset = false) {
     56         if (reset)
     57             rawMode.checked = false;
     58         if (!reset && raw.value === snapshot) {
     59             render();
     60             return;
     61         }
     62         try {
     63             rows = literals(JSON.parse(raw.value)).map(original => {
     64                 const input = document.createElement('textarea'), text = printLiteral(original);
     65                 input.value = text;
     66                 return { input, original, text };
     67             });
     68             snapshot = raw.value;
     69         }
     70         catch {
     71             rawMode.checked = true;
     72         }
     73         render();
     74     }
     75     add.onclick = () => { const input = document.createElement('textarea'); input.value = 't'; rows.push({ input, text: 't' }); render(); sync(); input.focus(); };
     76     raw.oninput = () => { snapshot = ''; };
     77     rawMode.onchange = () => {
     78         if (!rawMode.checked)
     79             refresh();
     80         else {
     81             try {
     82                 raw.value = JSON.stringify(values());
     83                 snapshot = raw.value;
     84             }
     85             catch (error) {
     86                 rawMode.checked = false;
     87                 document.getElementById('result').textContent = `Correct the argument before switching editors: ${error instanceof Error ? error.message : String(error)}`;
     88             }
     89         }
     90         render();
     91     };
     92     refresh();
     93     return {
     94         refresh,
     95         labels(value) { labels = value; },
     96         disable(value) { disabled = value; refresh(); },
     97         read() {
     98             if (raw.value.length > 16000)
     99                 throw new Error('Argument text limit exceeded');
    100             if (rawMode.checked || raw.value !== snapshot)
    101                 return literals(JSON.parse(raw.value));
    102             const result = values();
    103             if (JSON.stringify(result).length > 16000)
    104                 throw new Error('Argument text limit exceeded');
    105             sync();
    106             return result;
    107         },
    108     };
    109 }