会话线程 thread
AI 对话组件。三层同源:无头内核给出解剖与状态机,Vue 组件与自定义元素只是它的两层外壳,行为完全一致。
示例
基础用法
root 的高度由外部给定,滚动才发生在 viewport 里面;root / viewport / content 三层缺一不可
用户:这个对话区是怎么分层的?
助手:root 定框,viewport 负责滚动,content 包住全部消息。
用户:为什么一定要给高度?
助手:不给一个确定的框,内容永远不溢出,滚动与粘底都无从谈起。
用户:消息本身归谁管?
助手:归你。组件不碰数据,content 里放什么都行。
粘底与回到底部
内容长高时自动跟到底;往上滚一下当场撒手,回到底部按钮随即露出,滚回阈值内又自动粘上
用户:粘底是按什么判的?
助手:按滚动位置离底还差多少像素,差值落在阈值内就算在底。
用户:我自己往上滚一段呢?
助手:当场撒手,此后再长多少都不跟,右下角给你一条回去的路。
用户:滚回去要不要再点一下按钮?
助手:不用,滚进阈值内的那一下自动重新粘上。
运行态与播报
status 由宿主持有,组件只把它透出成 data-status;viewport 恒 aria-live="off",播报只发生在 live-region 里
用户:帮我写一段开场白。
触底加载更多
stick-change 报到底,宿主据此去取下一页;先往上滚一段再滚回底部,取回来的消息追加在后面
第 1 条 · 先往上滚一段,再滚回底部
第 2 条 · 先往上滚一段,再滚回底部
第 3 条 · 先往上滚一段,再滚回底部
第 4 条 · 先往上滚一段,再滚回底部
第 5 条 · 先往上滚一段,再滚回底部
第 6 条 · 先往上滚一段,再滚回底部
第 7 条 · 先往上滚一段,再滚回底部
第 8 条 · 先往上滚一段,再滚回底部
自己画回到底部的入口
root 的作用域插槽给出 showScrollButton 与 scrollToBottom,不用内置那颗浮动按钮也能拼出一条自己的提示栏
第 1 条 · 往上滚一段,下面那条提示栏就露出来
第 2 条 · 往上滚一段,下面那条提示栏就露出来
第 3 条 · 往上滚一段,下面那条提示栏就露出来
第 4 条 · 往上滚一段,下面那条提示栏就露出来
第 5 条 · 往上滚一段,下面那条提示栏就露出来
第 6 条 · 往上滚一段,下面那条提示栏就露出来
第 7 条 · 往上滚一段,下面那条提示栏就露出来
第 8 条 · 往上滚一段,下面那条提示栏就露出来
第 9 条 · 往上滚一段,下面那条提示栏就露出来
第 10 条 · 往上滚一段,下面那条提示栏就露出来
第 11 条 · 往上滚一段,下面那条提示栏就露出来
第 12 条 · 往上滚一段,下面那条提示栏就露出来
会话页
线程管滚动与粘底,编辑器管收话:发出去先落一条自己的消息,回话一段段写进来时消息区自动跟到底
助手:有什么想问的?
向上加载更早的消息
视口的滚动事件直接监听:滚到接近顶部就去取上一页,取回来的插在最前面
第 33 条 · 会话记录
第 34 条 · 会话记录
第 35 条 · 会话记录
第 36 条 · 会话记录
第 37 条 · 会话记录
第 38 条 · 会话记录
第 39 条 · 会话记录
第 40 条 · 会话记录
跳到指定的一条
视口节点与消息节点都在宿主手上:滚到顶、定位到某一条都是一次普通的 DOM 操作
第 1 条 · 会话记录
第 2 条 · 会话记录
第 3 条 · 会话记录
第 4 条 · 会话记录
第 5 条 · 会话记录
第 6 条 · 会话记录
第 7 条 · 会话记录
第 8 条 · 会话记录
第 9 条 · 会话记录
第 10 条 · 会话记录
第 11 条 · 会话记录
第 12 条 · 会话记录
第 13 条 · 会话记录
第 14 条 · 会话记录
第 15 条 · 会话记录
第 16 条 · 会话记录
第 17 条 · 会话记录
第 18 条 · 会话记录
第 19 条 · 会话记录
第 20 条 · 会话记录
第 21 条 · 会话记录
第 22 条 · 会话记录
第 23 条 · 会话记录
第 24 条 · 会话记录
产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-thread> |
| Vue 组件 | XhThreadContent XhThreadLiveRegion XhThreadRoot XhThreadScrollButton XhThreadViewport |
| 组合式函数 | useThread |
| 状态机 | threadMachine |
| 皮肤 | @xihan-ui/styles/thread.css |
解剖
部件名即 data-part 属性值,也是皮肤的选择器。加粗的是必备部件,不渲染它组件不工作(Web Components 适配器会在诊断通道上报 wc.missing-part)。
data-scope="thread":root · viewport · content · scroll-button · live-region
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
status | ThreadStatus | 这一轮的运行态,由宿主传入。 | |
threshold | number | 距底多少 px 视为在底,缺省用粘底原语的默认值。 | |
translations | Partial<ThreadTranslations> | ||
onStickChange | (details: ThreadStickChangeDetails) => void | 粘底状态变化时通知宿主。 |
状态机
状态:idle
事件:STICK.CHANGE · SCROLL_TO_BOTTOM
connect API
useThread 产出的对象。getXxxProps() 铺到对应部件的宿主元素上,其余是可读状态与操作入口。
| 成员 | 类型 | 说明 |
|---|---|---|
status | ThreadStatus | |
atBottom | boolean | |
sticking | boolean | |
showScrollButton | boolean | 是否显示回到底部按钮,不在底部时为 true。 |
scrollToBottom | () => void | |
getRootProps | () => T['element'] | |
getViewportProps | () => T['element'] | |
getContentProps | () => T['element'] | |
getScrollButtonProps | () => T['button'] | |
getLiveRegionProps | () => T['element'] |
键盘
规格出处:W3C APG
| 按键 | 生效条件 | 行为 |
|---|---|---|
Tab | 焦点进入消息区 | 消息区自身可聚焦,方向键/PageUp/PageDown 交给浏览器滚动,组件不接管 |
Space / Enter | 焦点在"回到底部"按钮上 | 滚回底部并重新粘附 |
