tabs: extract scheduleField() helper — collapse 8 label/caption sites
Every field on the new- and edit-schedule forms (plus buildIntervalComposer
and buildTargetChips) builds the same wrapper shape:
el('label', { class: 'schedule-field' },
el('span', { class: 'schedule-field-label' }, '…'))
then appends an input. Eight identical-modulo-text sites. Extracting one
small helper:
function scheduleField(labelText, ...children) {
return el('label', { class: 'schedule-field' },
el('span', { class: 'schedule-field-label' }, labelText),
...children);
}
drops the per-site cost to a single `form_.append(scheduleField('caption',
input));` line. -10 LOC net; no behaviour change.
This commit is contained in:
parent
1d7feeb029
commit
bc1c910044
1 changed files with 20 additions and 30 deletions
|
|
@ -2004,6 +2004,18 @@ window.marked = marked;
|
|||
}
|
||||
return n;
|
||||
}
|
||||
// Label + caption wrapper shared by every field on the schedule
|
||||
// forms — `<label class="schedule-field"><span
|
||||
// class="schedule-field-label">…</span>…children…</label>`. Variadic
|
||||
// children land inside the label after the caption span, which is
|
||||
// what every caller wants (the input + any preset/parts/preview
|
||||
// sub-rows live next to the caption).
|
||||
function scheduleField(labelText, ...children) {
|
||||
return el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, labelText),
|
||||
...children);
|
||||
}
|
||||
|
||||
// Interval composer (#466) — shared between the new-schedule form
|
||||
// and the edit-schedule form (#474). Builds the preset chip row +
|
||||
// d/h/m/s sub-fields + live preview, and returns helpers to read
|
||||
|
|
@ -2017,8 +2029,7 @@ window.marked = marked;
|
|||
namePrefix = 'interval_',
|
||||
initialSeconds = 0,
|
||||
} = {}) {
|
||||
const wrapper = el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, label));
|
||||
const wrapper = scheduleField(label);
|
||||
const presets = [
|
||||
['1m', 60], ['5m', 300], ['15m', 900], ['30m', 1800],
|
||||
['1h', 3600], ['6h', 21600], ['12h', 43200],
|
||||
|
|
@ -2142,8 +2153,6 @@ window.marked = marked;
|
|||
// explicitly uncheckable. New-schedule callers pass `[]`.
|
||||
for (const n of extraNames) if (!candidates.includes(n)) candidates.push(n);
|
||||
|
||||
const label = el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, 'targets'));
|
||||
const box = el('div', { class: 'schedule-targets' });
|
||||
for (const name of candidates) {
|
||||
const id_ = idPrefix + name;
|
||||
|
|
@ -2154,8 +2163,7 @@ window.marked = marked;
|
|||
box.append(el('label', { class: 'schedule-target-chip', for: id_ },
|
||||
cb, el('span', {}, name)));
|
||||
}
|
||||
label.append(box);
|
||||
return label;
|
||||
return scheduleField('targets', box);
|
||||
}
|
||||
|
||||
function renderScheduleNewForm() {
|
||||
|
|
@ -2181,17 +2189,12 @@ window.marked = marked;
|
|||
checked: new Set(carry.targets),
|
||||
}));
|
||||
|
||||
const bodyLabel = el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, 'prompt body'));
|
||||
const bodyInput = el('textarea', {
|
||||
name: 'body', rows: '4', required: 'required', placeholder: 'message delivered to each target at fire time',
|
||||
});
|
||||
bodyInput.value = carry.body;
|
||||
bodyLabel.append(bodyInput);
|
||||
form_.append(bodyLabel);
|
||||
form_.append(scheduleField('prompt body', bodyInput));
|
||||
|
||||
const firstFireLabel = el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, 'first fire'));
|
||||
const firstFireInput = el('input', {
|
||||
type: 'datetime-local', name: 'first_fire', required: 'required',
|
||||
});
|
||||
|
|
@ -2200,8 +2203,7 @@ window.marked = marked;
|
|||
// immediately on a stale-clock accident).
|
||||
firstFireInput.value = carry.first_fire
|
||||
|| isoForDatetimeLocal(new Date(Date.now() + 5 * 60 * 1000));
|
||||
firstFireLabel.append(firstFireInput);
|
||||
form_.append(firstFireLabel);
|
||||
form_.append(scheduleField('first fire', firstFireInput));
|
||||
|
||||
// Interval composer (#466). See `buildIntervalComposer` below —
|
||||
// shared between the new-schedule form and the edit-schedule form
|
||||
|
|
@ -2227,14 +2229,11 @@ window.marked = marked;
|
|||
}
|
||||
intervalCx.updatePreview();
|
||||
|
||||
const descLabel = el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, 'description (optional)'));
|
||||
const descInput = el('input', {
|
||||
type: 'text', name: 'description', placeholder: 'shown on the schedule card',
|
||||
});
|
||||
descInput.value = carry.description;
|
||||
descLabel.append(descInput);
|
||||
form_.append(descLabel);
|
||||
form_.append(scheduleField('description (optional)', descInput));
|
||||
|
||||
const actions = el('div', { class: 'schedule-actions' });
|
||||
const submit = el('button', { type: 'submit', class: 'btn btn-spawn' }, '+ qu3ue prompt');
|
||||
|
|
@ -2470,32 +2469,23 @@ window.marked = marked;
|
|||
|
||||
const carry = scheduleEditCarry.get(s.id) || {};
|
||||
|
||||
const bodyLabel = el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, 'body'));
|
||||
const bodyInput = el('textarea', { name: 'body', rows: '4', required: 'required' });
|
||||
bodyInput.value = carry.body !== undefined ? carry.body : s.body;
|
||||
bodyLabel.append(bodyInput);
|
||||
form_.append(bodyLabel);
|
||||
form_.append(scheduleField('body', bodyInput));
|
||||
|
||||
const descLabel = el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, 'description (blank to clear)'));
|
||||
const descInput = el('input', { type: 'text', name: 'description' });
|
||||
descInput.value = carry.description !== undefined
|
||||
? carry.description
|
||||
: (s.description || '');
|
||||
descLabel.append(descInput);
|
||||
form_.append(descLabel);
|
||||
form_.append(scheduleField('description (blank to clear)', descInput));
|
||||
|
||||
const firstFireLabel = el('label', { class: 'schedule-field' },
|
||||
el('span', { class: 'schedule-field-label' }, 'next fire'));
|
||||
const firstFireInput = el('input', {
|
||||
type: 'datetime-local', name: 'next_fire', required: 'required',
|
||||
});
|
||||
firstFireInput.value = carry.next_fire !== undefined
|
||||
? carry.next_fire
|
||||
: isoForDatetimeLocal(new Date(s.next_fire_at_unix * 1000));
|
||||
firstFireLabel.append(firstFireInput);
|
||||
form_.append(firstFireLabel);
|
||||
form_.append(scheduleField('next fire', firstFireInput));
|
||||
|
||||
const intervalCx = buildIntervalComposer({
|
||||
label: 'interval (blank / all-zero = flip to one-shot)',
|
||||
|
|
|
|||
Loading…
Reference in a new issue