Add column and row management features to comparison table block

This commit is contained in:
Matt Batchelder
2026-02-21 22:52:38 -05:00
parent 5ea06c2332
commit c91975db12

View File

@@ -1705,6 +1705,74 @@ reg('oribi/comparison-table', {
var a = props.attributes, s = props.setAttributes;
var cols = a.columns || [];
var rows = a.rows || [];
/* ── Column helpers ─────────────────────────────────────── */
function updateCol(idx, val) {
var c = cols.slice(); c[idx] = val; s({ columns: c });
}
function addCol() {
var newRows = rows.map(function(r) {
if (r.group) return r;
return Object.assign({}, r, { values: (r.values || []).concat([false]) });
});
s({ columns: cols.concat(['Plan']), rows: newRows });
}
function removeCol(idx) {
var c = cols.slice(); c.splice(idx, 1);
var newRows = rows.map(function(r) {
if (r.group) return r;
var v = (r.values || []).slice(); v.splice(idx, 1);
return Object.assign({}, r, { values: v });
});
s({ columns: c, rows: newRows });
}
/* ── Row helpers ────────────────────────────────────────── */
function updateRow(idx, key, val) {
var r = rows.slice();
r[idx] = Object.assign({}, r[idx]);
r[idx][key] = val;
s({ rows: r });
}
function updateCell(ri, ci, val) {
var r = rows.slice();
r[ri] = Object.assign({}, r[ri]);
var v = (r[ri].values || []).slice();
v[ci] = val;
r[ri].values = v;
s({ rows: r });
}
function toggleCell(ri, ci) {
var val = (rows[ri].values || [])[ci];
updateCell(ri, ci, val === true ? false : val === false ? true : true);
}
function switchCellToText(ri, ci) { updateCell(ri, ci, ''); }
function addFeatureRow() {
var vals = cols.map(function() { return false; });
s({ rows: rows.concat([{ feature: 'New feature', values: vals }]) });
}
function addGroupRow() {
s({ rows: rows.concat([{ group: 'New Group' }]) });
}
function removeRow(idx) {
var r = rows.slice(); r.splice(idx, 1); s({ rows: r });
}
function moveRow(idx, dir) {
var t = idx + dir;
if (t < 0 || t >= rows.length) return;
var r = rows.slice();
var tmp = r[idx]; r[idx] = r[t]; r[t] = tmp;
s({ rows: r });
}
/* ── Inline styles for editor controls ──────────────────── */
var inputStyle = { width: '100%', padding: '4px 6px', border: '1px solid #ddd', borderRadius: '3px', fontSize: '13px', background: 'transparent', boxSizing: 'border-box' };
var thInputStyle = Object.assign({}, inputStyle, { fontWeight: 600, textAlign: 'center' });
var smallBtnStyle = { background: 'none', border: 'none', cursor: 'pointer', padding: '2px 4px', fontSize: '11px', lineHeight: 1, opacity: 0.6, verticalAlign: 'middle' };
var rowCtrlStyle = { whiteSpace: 'nowrap', width: '1%', padding: '4px', verticalAlign: 'middle', border: 'none', background: 'transparent' };
var cellBtnStyle = { background: 'none', border: '1px solid #ddd', borderRadius: '3px', cursor: 'pointer', padding: '2px 6px', fontSize: '13px', lineHeight: '1.4', margin: '0 1px' };
/* ── Render ─────────────────────────────────────────────── */
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Table Settings' },
@@ -1712,6 +1780,21 @@ reg('oribi/comparison-table', {
{ label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' }
], onChange: function(v){s({variant:v});} }),
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} })
),
el(PB, { title: 'Columns', initialOpen: false },
cols.map(function(col, i) {
return el('div', { key: i, style: { display: 'flex', gap: '4px', marginBottom: '8px', alignItems: 'center' } },
el(TC, { label: '', value: col, onChange: function(v){ updateCol(i, v); }, style: { flex: 1 } }),
el(Btn, { isDestructive: true, isSmall: true, onClick: function(){ removeCol(i); } }, '\u2715')
);
}),
el(Btn, { isSecondary: true, isSmall: true, onClick: addCol }, '+ Add Column')
),
el(PB, { title: 'Add Rows', initialOpen: false },
el('div', { style: { display: 'flex', gap: '8px' } },
el(Btn, { isSecondary: true, isSmall: true, onClick: addFeatureRow }, '+ Feature Row'),
el(Btn, { isSecondary: true, isSmall: true, onClick: addGroupRow }, '+ Group Row')
)
)
),
el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' },
@@ -1726,26 +1809,62 @@ reg('oribi/comparison-table', {
el('thead', null,
el('tr', null,
el('th', { className: 'comparison-feature-col' }, 'Feature'),
cols.map(function(col, i) { return el('th', { key: i }, col); })
cols.map(function(col, i) {
return el('th', { key: i },
el('input', { type: 'text', value: col, style: thInputStyle, onChange: function(e){ updateCol(i, e.target.value); } })
);
}),
el('th', { style: rowCtrlStyle })
)
),
el('tbody', null,
rows.map(function(row, i) {
rows.map(function(row, ri) {
if (row.group) {
return el('tr', { key: i, className: 'comparison-group-row' },
el('td', { colSpan: cols.length + 1 }, row.group)
return el('tr', { key: ri, className: 'comparison-group-row' },
el('td', { colSpan: cols.length + 1 },
el('input', { type: 'text', value: row.group, style: Object.assign({}, inputStyle, { fontWeight: 700 }), onChange: function(e){ updateRow(ri, 'group', e.target.value); } })
),
el('td', { style: rowCtrlStyle },
el('button', { style: smallBtnStyle, onClick: function(){ moveRow(ri, -1); }, title: 'Move up' }, '\u25B2'),
el('button', { style: smallBtnStyle, onClick: function(){ moveRow(ri, 1); }, title: 'Move down' }, '\u25BC'),
el('button', { style: Object.assign({}, smallBtnStyle, { color: '#b00' }), onClick: function(){ removeRow(ri); }, title: 'Remove row' }, '\u2715')
)
);
}
return el('tr', { key: i },
el('td', { className: 'comparison-feature-name' }, row.feature || ''),
(row.values || []).map(function(val, j) {
return el('td', { key: j, className: 'comparison-cell' },
val === true ? '\u2713' : val === false ? '\u2014' : String(val)
return el('tr', { key: ri },
el('td', { className: 'comparison-feature-name' },
el('input', { type: 'text', value: row.feature || '', style: inputStyle, placeholder: 'Feature name\u2026', onChange: function(e){ updateRow(ri, 'feature', e.target.value); } })
),
(row.values || []).map(function(val, ci) {
if (typeof val === 'boolean') {
return el('td', { key: ci, className: 'comparison-cell', style: { textAlign: 'center' } },
el('button', { style: Object.assign({}, cellBtnStyle, { color: val ? '#2e7d32' : '#c62828' }), onClick: function(){ toggleCell(ri, ci); }, title: 'Toggle \u2713/\u2717' },
val ? '\u2713' : '\u2717'
),
el('button', { style: Object.assign({}, smallBtnStyle, { fontSize: '10px' }), onClick: function(){ switchCellToText(ri, ci); }, title: 'Switch to text' }, 'Aa')
);
}
return el('td', { key: ci, className: 'comparison-cell' },
el('div', { style: { display: 'flex', alignItems: 'center', gap: '2px' } },
el('input', { type: 'text', value: String(val), style: Object.assign({}, inputStyle, { flex: 1, minWidth: '60px' }), placeholder: 'Value\u2026', onChange: function(e){ updateCell(ri, ci, e.target.value); } }),
el('button', { style: Object.assign({}, smallBtnStyle, { color: '#2e7d32' }), onClick: function(){ updateCell(ri, ci, true); }, title: 'Set as \u2713' }, '\u2713'),
el('button', { style: Object.assign({}, smallBtnStyle, { color: '#c62828' }), onClick: function(){ updateCell(ri, ci, false); }, title: 'Set as \u2717' }, '\u2717')
)
);
})
}),
el('td', { style: rowCtrlStyle },
el('button', { style: smallBtnStyle, onClick: function(){ moveRow(ri, -1); }, title: 'Move up' }, '\u25B2'),
el('button', { style: smallBtnStyle, onClick: function(){ moveRow(ri, 1); }, title: 'Move down' }, '\u25BC'),
el('button', { style: Object.assign({}, smallBtnStyle, { color: '#b00' }), onClick: function(){ removeRow(ri); }, title: 'Remove row' }, '\u2715')
)
);
})
)
),
el('div', { style: { display: 'flex', gap: '8px', marginTop: '12px', justifyContent: 'center' } },
el(Btn, { isSecondary: true, isSmall: true, onClick: addFeatureRow }, '+ Feature Row'),
el(Btn, { isSecondary: true, isSmall: true, onClick: addGroupRow }, '+ Group Row'),
el(Btn, { isSecondary: true, isSmall: true, onClick: addCol }, '+ Column')
)
)
)