iter(g300): 修正 toast 悬停暂停从未生效
This commit is contained in:
@@ -4,6 +4,7 @@ import type { ToastProps } from './types.js';
|
||||
let host: HTMLElement | null = null; // 全局右上角堆叠容器:首条消息出现时懒创建
|
||||
let seq = 0; // 自增序号:为每条 toast 生成唯一 id,辅助无障碍定位
|
||||
const MAX_STACK = 4; // 同屏最多条数:超出即挤掉最早一条,避免刷屏淹页面
|
||||
const PAUSE_FLOOR = 400; // 恢复时的最短剩余毫秒:悬停前已近归零也不至于松手即消失
|
||||
|
||||
/*** 确保堆叠容器存在:fixed 挂在 body 末尾,后续消息依次向上堆叠 ***/
|
||||
function ensureHost(): HTMLElement {
|
||||
@@ -43,10 +44,23 @@ export function showToast(props: ToastProps): HTMLElement {
|
||||
stack.appendChild(item);
|
||||
requestAnimationFrame(() => item.classList.add('ua-open')); // 下一帧入场,保证过渡生效
|
||||
const arm = (ms: number): void => { startedAt = Date.now(); timer = window.setTimeout(() => dismiss(item), ms); }; // 重新武装倒计时
|
||||
/*** 暂停倒计时:扣掉本段已走时长,清空句柄(重复进入不会二次扣减) ***/
|
||||
const pause = (): void => {
|
||||
if (!timer) return; // 已暂停 / 常驻(duration 为 0)时无计时器可停
|
||||
window.clearTimeout(timer);
|
||||
timer = 0;
|
||||
remain = Math.max(PAUSE_FLOOR, remain - (Date.now() - startedAt));
|
||||
};
|
||||
/*** 恢复倒计时:按剩余时长重新武装 ***/
|
||||
const resume = (): void => { if (!timer && remain > 0) arm(remain); };
|
||||
if (duration > 0) {
|
||||
arm(duration);
|
||||
item.addEventListener('pointerenter', () => { window.clearTimeout(timer); remain -= Date.now() - startedAt; }); // 悬停暂停:记下剩余时长
|
||||
item.addEventListener('pointerleave', () => arm(remain)); // 移出后按剩余时长继续
|
||||
item.addEventListener('pointerenter', pause); // 悬停停留阅读:倒计时暂停
|
||||
item.addEventListener('pointerleave', resume); // 移出后按剩余时长继续
|
||||
item.addEventListener('focusin', pause); // 键盘 Tab 进关闭 / 操作按钮时同样暂停
|
||||
item.addEventListener('focusout', (e) => {
|
||||
if (!item.contains(e.relatedTarget as Node | null)) resume(); // 焦点还在卡片内部(按钮间切换)时不恢复
|
||||
});
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -1128,7 +1128,7 @@
|
||||
{
|
||||
"n": "closable",
|
||||
"t": "boolean",
|
||||
"d": "显示关闭按钮,点 × 立即收起;缺省 false(仅可关闭的 toast 接收指针事件,其余保持穿透)",
|
||||
"d": "显示关闭按钮,点 × 立即收起;缺省 false(任意 toast 都接收指针,悬停或聚焦即暂停倒计时)",
|
||||
"df": "false"
|
||||
},
|
||||
{
|
||||
@@ -1153,6 +1153,7 @@
|
||||
"steps": [
|
||||
"直接调用语义工厂:toast.info / success / warning / error(message, duration?),无需先建容器",
|
||||
"不传 duration 时默认停留 3 秒后自动淡出并移除节点",
|
||||
"鼠标悬停或键盘 Tab 进卡片时倒计时暂停,移出 / 焦点离开后按剩余时长续走(剩余不足 400ms 时补到 400ms,避免松手即消失);宿主容器仍穿透,只有卡片本体这一小块可命中",
|
||||
"多条消息在右上角自动纵向堆叠,互不遮挡",
|
||||
"需要完整入参(含 variant)时用 showToast(props);组件挂载容器由内部懒创建,样式随 ui.css 一起引入",
|
||||
"同屏最多 4 条,超出自动挤掉最早一条;需要用户主动确认的消息用 showToast({ closable: true }) 给个 ×",
|
||||
|
||||
@@ -1543,16 +1543,14 @@ button.ua-step-dot:hover {
|
||||
font-size: var(--ua-font-size-md);
|
||||
opacity: 0;
|
||||
transform: translateX(var(--ua-translate-12));
|
||||
/* 自己接指针:悬停可暂停倒计时(宿主仍穿透,只有卡片本体这一小块可命中) */
|
||||
pointer-events: auto;
|
||||
transition: opacity var(--ua-duration-base) var(--ua-ease-out), transform var(--ua-duration-base) var(--ua-ease-out);
|
||||
}
|
||||
.ua-toast.ua-open {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
/* 仅带关闭按钮的 toast 接收指针,其余保持穿透不挡页面 */
|
||||
.ua-toast.is-closable {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.ua-toast-close {
|
||||
padding: 0 var(--ua-space-05);
|
||||
border: none;
|
||||
@@ -4219,9 +4217,6 @@ button.ua-step-dot:hover {
|
||||
}
|
||||
|
||||
/* —— toast 操作按钮(撤销类) —— */
|
||||
.ua-toast.is-action {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.ua-toast-action {
|
||||
padding: 0;
|
||||
border: none;
|
||||
|
||||
Reference in New Issue
Block a user