Files

117 lines
5.5 KiB
JavaScript
Raw Permalink Normal View History

/* ============================================================================
通用「新建弹框」开关(v81)
----------------------------------------------------------------------------
用户原话(2026-10-22):
「首页上的其他卡片也应该有这些基本功能。比如电影观影,它有一个"新增电影"的
功能;如果是纪念日,就有一个"新增纪念日"。实际上,就是把原来模块里弹的那个框,
现在拿到这边来。目的就是为了在首页上就能快速完成一定工作,而不用进到模块里面。」
v78 给「近期任务」写过一份 `task_add.js`(只能管那一张弹框)。这一批要把
观影、纪念日也搬上来 ⇒ 三份几乎一样的脚本 = 三处迟早不同步。所以抽这一份**通用的**:
每张弹框只需声明自己的「容器 id / 开按钮 / 关按钮」三个名字。
用法(在弹框模板里给容器加两个属性即可,不需要改 JS):
<div class="cal-modal" id="mvAddCard"
data-fb-modal="mvAddCard" ← 容器 id(= 触发器的 data-fb-modal-open 值)
data-fb-modal-open="[data-mv-add-open]"
data-fb-modal-close="[data-mv-add-close]"
hidden>
或者更省事:容器上加 `data-fb-add-modal`,触发器上加
`data-fb-add-open="#<容器id>"`、`data-fb-add-close` —— 全站约定见下。
行为(与站内其它浮层一致,v78 那套原样保留):
· 点触发器开、点 ✕/取消 关、点**遮罩**关、点面板内部**不**关、Esc 关;
· Esc 时若 antd 的日期面板开着,**先让 antd 收自己的**(否则一次 Esc 关两层,用户懵);
· 打开那一刻才升级日期控件 —— 弹框初始 `hidden`,antd 面板拿到 0 尺寸会定位到
屏幕左上角(task_add.js 与 tasks/show.php 都为此专门 boot 过,同一条坑)。
⚠️ 全站加载,页面里没有带 `data-fb-add-modal` 的容器时**什么都不做**(几行判断)。
⚠️ 幂等闸门 `window.__fbAddModalBound`:脚本可能被重复引入(AJAX 换正文、模板重复),
不加会绑两遍 ⇒ 点一次开两次、关两次。
⚠️ 为什么不用内联 script:内联脚本在 AJAX 换正文时**不会被重跑**(v42/v47 连着栽过)。
============================================================================ */
(function () {
'use strict';
if (window.__fbAddModalBound) { return; }
window.__fbAddModalBound = true;
/* 容器:`data-fb-add-modal` 自带 id 也算(省去写两遍) */
function containers() {
return Array.prototype.slice.call(
document.querySelectorAll('[data-fb-add-modal]'));
}
function antdOpen() {
var d = document.querySelector('.ant-picker-dropdown');
return !!(d && !d.classList.contains('ant-picker-dropdown-hidden'));
}
/* 打开时要做的"补动作":日期控件必须等可见之后再升级(hidden 时宽高为 0)。
两步都要:先绑自建下拉(兜底),再挂 antd(正主);两个函数都自判幂等。 */
function wake(c) {
if (window.fbUpgradeRange) { window.fbUpgradeRange(); }
if (window.fbBootAntd) { window.fbBootAntd(); }
/* 焦点落到第一颗输入框:键盘用户不用先 Tab 一遍 */
var f = c.querySelector('input:not([type=hidden]), textarea, select');
if (f && !f.disabled) { try { f.focus(); } catch (e) {} }
}
function show(c, on) {
if (!c) { return; }
c.hidden = !on;
/* 触发器的 aria-expanded 跟着走(有就更新,没有不报错) */
var sel = c.getAttribute('data-fb-modal-open');
if (sel) {
Array.prototype.forEach.call(document.querySelectorAll(sel), function (b) {
b.setAttribute('aria-expanded', on ? 'true' : 'false');
});
}
if (on) { wake(c); }
}
/* 由点击位置反推"该开/该关哪一张":
· 触发器上写 `data-fb-add-open="#id"`(显式指定容器);
· 关闭按钮/遮罩则就近找祖先里的 `[data-fb-add-modal]`。 */
function targetFor(t) {
if (t.closest('[data-fb-add-close]')) {
return { c: t.closest('[data-fb-add-modal]'), on: false };
}
var opener = t.closest('[data-fb-add-open]');
if (opener) {
var sel = opener.getAttribute('data-fb-add-open') || '';
var c = sel ? document.querySelector(sel) : null;
return { c: c, on: true };
}
return null;
}
document.addEventListener('click', function (e) {
var t = e.target;
if (!t || !t.closest) { return; }
var hit = targetFor(t);
if (hit) {
if (hit.c) { e.preventDefault(); }
show(hit.c, hit.on);
return;
}
/* 点遮罩关、点面板内不关 —— 容器本身就是遮罩层,所以只认 === */
if (t.hasAttribute && t.hasAttribute('data-fb-add-modal')) {
show(t, false);
}
});
document.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') { return; }
var open = containers().filter(function (c) { return !c.hidden; });
if (!open.length) { return; }
if (antdOpen()) { return; } // 让 antd 先收它自己的面板
show(open[open.length - 1], false);
});
/* 供其它脚本(提交成功后就地插入回包等)主动开合:
window.fbAddModal.open('#id') / .close('#id') */
window.fbAddModal = {
open: function (sel) { show(document.querySelector(sel), true); },
close: function (sel) { show(document.querySelector(sel), false); }
};
})();