feat(widgets): replicate 12 reference widgets for full Zibll parity

- rewrite schemas for posts, tab_posts, oneline_posts, hot_posts,
  term_cards, term_aggregation, media_cards, combo_info, partners,
  recent_comments, posts_toc and slider_media_combo to mirror the
  modern reference modules field by field (options, defaults,
  dependencies, min/max/step, subheadings)
- port reference renderers: hot ranking with TOP badges, category
  aggregation card, graphic cover cards, partners CSS marquee,
  combo info cards with auto band palette, recent comment list
- rename 分类与标签聚合卡片 to 分类聚合卡片 and restrict it to
  categories (no tags/topics branch in this project)
- drop topics filter fields and the topics query branch; align
  orderby value domain with supported post meta
- add searchable sortable terms admin field type and spinner unit
  suffix rendering
- unify widget pagination and lazy loading on the REST client with
  the runtime-owned ias reveal trigger; remove the legacy
  post_ajax_trigger dual path
- align section-navs TOC with the reference contract (wznav ids,
  3-heading minimum, zib-widget box)
This commit is contained in:
2026-09-05 11:28:00 +08:00
parent 9dc278bd83
commit b639d57c5b
25 changed files with 1388 additions and 446 deletions
+42
View File
@@ -12,6 +12,10 @@
axis: 'y',
update: renumberRepeaters,
});
$root.find('.aksrt-widget-terms__selected').sortable({
items: '.aksrt-widget-terms__chip',
update: () => $root.find('.aksrt-widget-terms').each((_i, el) => syncTerms(el)),
});
$root.find('input[type="range"]').each(function () {
const output = this.parentElement.querySelector('output');
if (output) this.addEventListener('input', () => { output.textContent = this.value; });
@@ -21,6 +25,25 @@
updatePlacementWarning($root);
}
/* terms 字段:以“已选标签”为唯一真源,同步隐藏输入(选择顺序)与复选框。 */
function syncTerms(wrap) {
const $wrap = $(wrap);
const ids = $wrap.find('.aksrt-widget-terms__chip').map((_i, el) => String($(el).data('term-id'))).get();
$wrap.find('.aksrt-widget-terms__value').val(ids.join(',')).trigger('change');
$wrap.find('.aksrt-widget-terms__option input').each(function () {
this.checked = ids.indexOf(String(this.value)) !== -1;
});
}
function addTermChip(wrap, id, name) {
const $selected = $(wrap).find('.aksrt-widget-terms__selected');
if ($selected.find(`.aksrt-widget-terms__chip[data-term-id="${id}"]`).length) return;
const chip = $(`<span class="aksrt-widget-terms__chip" data-term-id="${id}"><span class="dashicons dashicons-move" aria-hidden="true"></span></span>`);
chip.append(document.createTextNode(name));
chip.append('<button type="button" class="button-link-delete aksrt-widget-terms__remove" aria-label="移除">×</button>');
$selected.append(chip);
}
function updateWidgetTitle(root) {
const $root = $(root);
const title = String($root.find('.aksrt-widget-field[data-field-id="title"] input').first().val() || '').trim();
@@ -117,6 +140,25 @@
$(this).closest('.aksrt-widget-repeater__row').remove();
renumberRepeaters.call($rows[0]);
})
.on('click', '.aksrt-widget-terms__remove', function () {
$(this).closest('.aksrt-widget-terms__chip').remove();
syncTerms($(this).closest('.aksrt-widget-terms'));
})
.on('change', '.aksrt-widget-terms__option input', function () {
const wrap = this.closest('.aksrt-widget-terms');
const option = this.closest('.aksrt-widget-terms__option');
const id = String(this.value);
const name = (option.querySelector('[data-term-name]') || option).getAttribute('data-term-name');
if (this.checked) addTermChip(wrap, id, name || id);
syncTerms(wrap);
})
.on('input', '.aksrt-widget-terms__search', function () {
const needle = this.value.trim().toLowerCase();
$(this).closest('.aksrt-widget-terms').find('.aksrt-widget-terms__option').each(function () {
const name = String(this.getAttribute('data-term-name') || '').toLowerCase();
this.hidden = needle !== '' && name.indexOf(needle) === -1;
});
})
.on('click', '.aksrt-widget-upload__choose', function () {
const $wrap = $(this).closest('.aksrt-widget-upload');
const frame = wp.media({ title: $wrap.data('media-library') === 'video' ? '选择视频' : '选择图片', library: $wrap.data('media-library') ? { type: $wrap.data('media-library') } : undefined, multiple: false });