From 1bf35dacdcf4dd204fc829504cddb787cc576b7d Mon Sep 17 00:00:00 2001 From: HIsheR Date: Mon, 26 Oct 2026 14:01:57 +0800 Subject: [PATCH] =?UTF-8?q?iter(g109):=20=E6=8F=90=E7=A4=BA=E6=B0=94?= =?UTF-8?q?=E6=B3=A1=E6=94=AF=E6=8C=81=E8=A7=A6=E6=91=B8=E7=82=B9=E6=8C=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/backlog.json | 8 ++++++++ .ua/index.json | 14 ++++++-------- llms-full.txt | 14 ++++++-------- llms.txt | 2 +- packages/skin/ui/src/tooltip.ts | 21 +++++++++++++++++++-- packages/skin/ui/ua.manifest.json | 12 +++++------- 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/.ua/backlog.json b/.ua/backlog.json index 535233d..043157b 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -1228,6 +1228,14 @@ "acceptance": "form 提交时统计不通字段数,顶部渲染 role=alert 的汇总条「有 N 项需要修改,已定位到第一处」(通过则隐藏);FormProps 新增 onError(count) 与 errorSummary(缺省 true,传 false 不插节点);文档参数表与演示说明同步;npm run check 全绿", "notes": "自补:无障碍与可用性;汇总条与逐项红框同源,均由一次提交的校验结果推导", "status": "done" + }, + { + "id": "g109", + "title": "提示气泡支持触摸屏点按", + "motivation": "tooltip 只挂 pointerenter / pointerleave:触摸屏上按下会触发 enter(延迟 150ms 出),抬手立刻触发 leave 收起 —— 等于气泡刚出来就被手指带走,用户根本没机会读;移动端等于该组件不可用", + "acceptance": "触摸指针(pointerType=touch)不走 hover 分支,改为按下即出场、抬手后延迟 1.2s 收起;鼠标与键盘路径行为不变;re-enter 时清掉上一轮的延迟收起计时;文档手把手与 api 说明补触摸语义;npm run check 全绿", + "notes": "自补:可用性;只在原文件加两个分支与一个计时器,未改签名", + "status": "open" } ] } diff --git a/.ua/index.json b/.ua/index.json index 305cb88..c743178 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T06:00:53.659Z", + "generatedAt": "2026-10-26T06:01:43.914Z", "repoRoot": ".", "packages": [ { @@ -161,7 +161,7 @@ "avatar(props):头像,有 src 渲染图片(alt 用名称)、无 src 用首字占位,size 可调 + 四档语义底色", "alert(props):提示条,variant 四档语义色 + 可选 title 与 closable 关闭按钮,role=alert 读屏可播报", "toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失(悬停暂停、移出续时),出入场微动画;showToast(props) 为完整入参版本(closable 显示关闭按钮,同屏最多 4 条)", - "tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起;视口一侧空间不足时自动翻转", + "tooltip(props, trigger):包裹式悬浮提示,hover / 键盘聚焦延迟出场,Esc 收起;触摸屏按下即出、抬手延迟 1.2s 收起,气泡 pointer-events: none 不挡点击", "popover(props, trigger):可交互气泡卡片,包裹触发器,默认点击开合 + 点外部 / Esc 收起,content 可放按钮等交互元素;视口一侧空间不足时自动翻转", "skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画", "pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,jumpable 时末尾可输入页码直接跳转,键盘可达;pageSizes 可切每页条数", @@ -1175,12 +1175,10 @@ ], "example": "tooltip({ content: '保存后立即生效' }, button({ label: '保存', onClick: save }))", "steps": [ - "先创建触发器元素,如 const btn = button({ label: '保存' })", - "用 tooltip(props, trigger) 包一层:返回带气泡的包裹元素,原触发器仍是独立节点", - "鼠标移入或键盘聚焦约 150ms 后出场,移出 / 失焦立即收起;按 Esc 可立刻关闭", - "气泡默认在上方,空间不足或需要避让时传 placement: 'bottom'", - "气泡 pointer-events: none 不抢指针;判读依赖 aria-describedby + role='tooltip',读屏可读", - "只做展示:不要在气泡里放可点击元素,需要交互请用 modal 或自绘弹层" + "把提示要挂在的元素作为第二个参数传入,返回的包裹节点替换原位置(气泡绝对定位,不占布局)", + "鼠标悬停或键盘聚焦后延迟 delay 毫秒出场,移出 / 失焦 / Esc 收起;空间不足时自动翻到另一侧", + "触摸屏没有 hover:手指按下即出场,抬手后停留 1.2 秒再收起,免得手指正挡着就消失", + "气泡设了 pointer-events: none,不会挡住下面的点击;aria-describedby 已关联,读屏能念出提示文案" ] }, { diff --git a/llms-full.txt b/llms-full.txt index f7d44d0..a2f8fee 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T06:00:53.659Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T06:01:43.914Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -585,12 +585,10 @@ tooltip({ content: '保存后立即生效' }, button({ label: '保存', onClick: 手把手: -1. 先创建触发器元素,如 const btn = button({ label: '保存' }) -2. 用 tooltip(props, trigger) 包一层:返回带气泡的包裹元素,原触发器仍是独立节点 -3. 鼠标移入或键盘聚焦约 150ms 后出场,移出 / 失焦立即收起;按 Esc 可立刻关闭 -4. 气泡默认在上方,空间不足或需要避让时传 placement: 'bottom' -5. 气泡 pointer-events: none 不抢指针;判读依赖 aria-describedby + role='tooltip',读屏可读 -6. 只做展示:不要在气泡里放可点击元素,需要交互请用 modal 或自绘弹层 +1. 把提示要挂在的元素作为第二个参数传入,返回的包裹节点替换原位置(气泡绝对定位,不占布局) +2. 鼠标悬停或键盘聚焦后延迟 delay 毫秒出场,移出 / 失焦 / Esc 收起;空间不足时自动翻到另一侧 +3. 触摸屏没有 hover:手指按下即出场,抬手后停留 1.2 秒再收起,免得手指正挡着就消失 +4. 气泡设了 pointer-events: none,不会挡住下面的点击;aria-describedby 已关联,读屏能念出提示文案 现场演示:apps/docs/demo.html#sec-tooltip @@ -1643,7 +1641,7 @@ API 速览: - avatar(props):头像,有 src 渲染图片(alt 用名称)、无 src 用首字占位,size 可调 + 四档语义底色 - alert(props):提示条,variant 四档语义色 + 可选 title 与 closable 关闭按钮,role=alert 读屏可播报 - toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失(悬停暂停、移出续时),出入场微动画;showToast(props) 为完整入参版本(closable 显示关闭按钮,同屏最多 4 条) -- tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起;视口一侧空间不足时自动翻转 +- tooltip(props, trigger):包裹式悬浮提示,hover / 键盘聚焦延迟出场,Esc 收起;触摸屏按下即出、抬手延迟 1.2s 收起,气泡 pointer-events: none 不挡点击 - popover(props, trigger):可交互气泡卡片,包裹触发器,默认点击开合 + 点外部 / Esc 收起,content 可放按钮等交互元素;视口一侧空间不足时自动翻转 - skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画 - pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,jumpable 时末尾可输入页码直接跳转,键盘可达;pageSizes 可切每页条数 diff --git a/llms.txt b/llms.txt index 0079bc2..7224a60 100644 --- a/llms.txt +++ b/llms.txt @@ -66,7 +66,7 @@ - avatar(props):头像,有 src 渲染图片(alt 用名称)、无 src 用首字占位,size 可调 + 四档语义底色 - alert(props):提示条,variant 四档语义色 + 可选 title 与 closable 关闭按钮,role=alert 读屏可播报 - toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失(悬停暂停、移出续时),出入场微动画;showToast(props) 为完整入参版本(closable 显示关闭按钮,同屏最多 4 条) - - tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起;视口一侧空间不足时自动翻转 + - tooltip(props, trigger):包裹式悬浮提示,hover / 键盘聚焦延迟出场,Esc 收起;触摸屏按下即出、抬手延迟 1.2s 收起,气泡 pointer-events: none 不挡点击 - popover(props, trigger):可交互气泡卡片,包裹触发器,默认点击开合 + 点外部 / Esc 收起,content 可放按钮等交互元素;视口一侧空间不足时自动翻转 - skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画 - pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,jumpable 时末尾可输入页码直接跳转,键盘可达;pageSizes 可切每页条数 diff --git a/packages/skin/ui/src/tooltip.ts b/packages/skin/ui/src/tooltip.ts index 159e3a3..d24f409 100644 --- a/packages/skin/ui/src/tooltip.ts +++ b/packages/skin/ui/src/tooltip.ts @@ -12,6 +12,7 @@ export function tooltip(props: TooltipProps, trigger: HTMLElement): HTMLElement const id = `ua-tip-${++seq}`; // 气泡唯一 id const delay = props.delay ?? 150; // 出场延迟毫秒;<=0 视为立即显示 let timer: number | null = null; // 出场延迟计时器句柄 + let holdTimer: number | null = null; // 触摸抬手后的延迟收起计时器 const show = () => { if (timer != null) window.clearTimeout(timer); // 重复进入先清旧计时 @@ -26,9 +27,22 @@ export function tooltip(props: TooltipProps, trigger: HTMLElement): HTMLElement window.clearTimeout(timer); timer = null; } + if (holdTimer != null) { // 触摸遗留的延迟收起一并取消,避免刚点开又被上一轮收掉 + window.clearTimeout(holdTimer); + holdTimer = null; + } open(false); }; + /*** 触摸抬手后延迟收起:手指离开屏幕时气泡正被人挡着,留一会儿再收 ***/ + const hideLater = (ms = 1200): void => { + if (holdTimer != null) window.clearTimeout(holdTimer); + holdTimer = window.setTimeout(() => { + holdTimer = null; + open(false); + }, ms); + }; + // —— 触发器与气泡 —— trigger.setAttribute('aria-describedby', id); // 无障碍:读屏把提示文案关联到触发器 const bubble = h( @@ -43,10 +57,13 @@ export function tooltip(props: TooltipProps, trigger: HTMLElement): HTMLElement ); const root = h('span', { class: 'ua-tip-wrap' }, trigger, bubble); - root.addEventListener('pointerenter', show); // 鼠标进入 - root.addEventListener('pointerleave', hide); // 鼠标离开 + root.addEventListener('pointerenter', (e: PointerEvent) => { if (e.pointerType !== 'touch') show(); }); // 鼠标进入(触摸交给下面的点按) + root.addEventListener('pointerleave', (e: PointerEvent) => { if (e.pointerType !== 'touch') hide(); }); // 鼠标离开 root.addEventListener('focusin', show); // 键盘聚焦(focusin 会冒泡) root.addEventListener('focusout', hide); // 失焦 + // —— 触摸屏:没有 hover,改点按。按下即出(不等 150ms),抬手留 1.2s 再收,否则手指正挡着气泡就没了 —— + root.addEventListener('pointerdown', (e: PointerEvent) => { if (e.pointerType === 'touch') show(); }); + root.addEventListener('pointerup', (e: PointerEvent) => { if (e.pointerType === 'touch') hideLater(); }); effect(() => { if (open()) place(flip(trigger, bubble, props.placement ?? 'top')); }); // 每次出场重算方位:空间不足自动翻转 root.addEventListener('keydown', (e: KeyboardEvent) => { if (e.key === 'Escape') { e.stopPropagation(); hide(); } // Esc 立即收起且不影响外层弹层 diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index aff93b1..ea10855 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -32,7 +32,7 @@ "avatar(props):头像,有 src 渲染图片(alt 用名称)、无 src 用首字占位,size 可调 + 四档语义底色", "alert(props):提示条,variant 四档语义色 + 可选 title 与 closable 关闭按钮,role=alert 读屏可播报", "toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失(悬停暂停、移出续时),出入场微动画;showToast(props) 为完整入参版本(closable 显示关闭按钮,同屏最多 4 条)", - "tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起;视口一侧空间不足时自动翻转", + "tooltip(props, trigger):包裹式悬浮提示,hover / 键盘聚焦延迟出场,Esc 收起;触摸屏按下即出、抬手延迟 1.2s 收起,气泡 pointer-events: none 不挡点击", "popover(props, trigger):可交互气泡卡片,包裹触发器,默认点击开合 + 点外部 / Esc 收起,content 可放按钮等交互元素;视口一侧空间不足时自动翻转", "skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画", "pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,jumpable 时末尾可输入页码直接跳转,键盘可达;pageSizes 可切每页条数", @@ -1046,12 +1046,10 @@ ], "example": "tooltip({ content: '保存后立即生效' }, button({ label: '保存', onClick: save }))", "steps": [ - "先创建触发器元素,如 const btn = button({ label: '保存' })", - "用 tooltip(props, trigger) 包一层:返回带气泡的包裹元素,原触发器仍是独立节点", - "鼠标移入或键盘聚焦约 150ms 后出场,移出 / 失焦立即收起;按 Esc 可立刻关闭", - "气泡默认在上方,空间不足或需要避让时传 placement: 'bottom'", - "气泡 pointer-events: none 不抢指针;判读依赖 aria-describedby + role='tooltip',读屏可读", - "只做展示:不要在气泡里放可点击元素,需要交互请用 modal 或自绘弹层" + "把提示要挂在的元素作为第二个参数传入,返回的包裹节点替换原位置(气泡绝对定位,不占布局)", + "鼠标悬停或键盘聚焦后延迟 delay 毫秒出场,移出 / 失焦 / Esc 收起;空间不足时自动翻到另一侧", + "触摸屏没有 hover:手指按下即出场,抬手后停留 1.2 秒再收起,免得手指正挡着就消失", + "气泡设了 pointer-events: none,不会挡住下面的点击;aria-describedby 已关联,读屏能念出提示文案" ] }, {