更新日志
本页由 Changesets 在发版时自动生成,不要手写。
所有公开包锁步发版、共用一个版本号,因此这里是全部包的统一变更记录。
0.1.1
Patch Changes
- 基础组件包的设计变量已包含在全局样式中,安装时不再额外拉取独立的 tokens 包。
0.1.0
Minor Changes
30f0008: 为后续迁入的组件准备图标与文案底座,现有组件的渲染结果不变。
- 新增内部模块
utils/icons.tsx:16 个 lucide 图标(lucide-static@1.47.0,ISC)与渲染辅助icon(name, { size }),统一 viewBox 24 / currentColor / 描边 2 / 圆头圆角 /aria-hidden。不从入口导出、不做pt-icon组件 —— 公开的图标仍由使用方经 slot 传入。现有组件的内联 SVG 都是 16 格自绘图形,与 lucide 不一致,本次不替换。致谢见根目录NOTICE。 LocaleMessages新增 24 个 key(combobox / command / pagination / breadcrumb / carousel / sidebar / scroller / promptPicker / toast / calendar / otp / resizable / attachment),zh-CN 与 en 齐全。经registerLocale()注册的自定义 locale 不受影响(接受 Partial,缺的 key 落回英文;显式传undefined的 key 现在也按缺失处理);但用LocaleMessages类型标注完整文案对象的代码会因缺这 24 个 key 编译失败,改成Partial<LocaleMessages>交给registerLocale()即可。t()新增可选第三个参数做{name}占位符插值,并导出类型MessageParams。
- 新增内部模块
1f0f8a3:
pt-button补齐两处 API:- 新属性
download:透传给内部的<a download>,只在有href且未禁用 / 未加载时生效。 空字符串同样透传(download=""表示用默认文件名),不写才是正常导航。 - 新 CSS 变量
--pt-button-padding-x:覆盖三档尺寸的默认横向内边距(sm 8px / md 12px / lg 16px), 在使用处兜底,设在祖先容器上即可作用到一排按钮。纯图标按钮与link变体恒为 0,不受它影响。
不设这个变量时样式与之前逐条相同。
- 新属性
4adc77e: 批 1 · C1.3:
pt-badge补齐可点形态,对齐源包 badge。- 新增
href/target/rel:给了href内部渲染成<a class="base" part="base">;target="_blank"且没给rel时自动补noreferrer noopener(与pt-button一致)。 - 新增
interactive(布尔,反射):内部渲染成<button type="button">,点击监听宿主的原生click。有href时视同打开。 - 可点形态才挂状态层(
default用反转版,其余变体用常规版)、手型光标、焦点环与移动端触控外扩; 1px 透明边框按描边宿主处理(--pt-state-layer-border: 1px)。纯展示的<span>外观与行为不变。 - 影子根开启
delegatesFocus,可点形态下element.focus()会落到内部的 a / button 上。
- 新增
793f852: pt-input 补齐搜索框与日期类(批 1 · C1.4):
type新增date/time/datetime-local/month/week,原样透传给原生 input,min/max/step按原生语义生效。type="search":前缀位置内置放大镜(prefix插槽的后备内容,给了prefix就替换掉),内部 input 标role="searchbox",去掉原生清除 ✕ 与装饰;有值时按 Esc 清空(先ptClear再ptChange('')),并截住这次 Esc 不让它冒泡关掉外层弹窗,值为空时不拦。- 新属性
clear-label(clearLabel):清空按钮的读屏文案,优先于 locale 注册表的input.clear。 - 新 CSS 变量
--pt-input-border-width(默认 1px),供输入组拼接时去掉单个输入框的描边。
f2da3ab: pt-select 家族补齐(批 1 · C1.5,对应 design-components 的 SelectGroup / SelectLabel / SelectSeparator):
- 新增子元素
pt-option-group:label属性渲染一行不可选的组标题(14/20 半粗,左右 8px、上下 6px), 影子树里是role="group"+aria-labelledby指向组标题,构成listbox > group > option。 partbase/label。组内选项照常参与选中、方向键与首字母跳转,组标题不参与匹配; 组内增删选项时会通知所属 pt-select 重新同步。 - pt-select 的列表里可以放
pt-separator(直接放或放在组里都行):上下 4px 间距、左右贯通弹层边缘 (窄屏抽屉下同样贯通),颜色默认换成--pt-bg-secondary(源包的 bg-muted),在分隔线自身上设--pt-separator-color仍可覆盖。 pt-option新增keywords(空格分隔的搜索关键词):只存储、反射,不影响渲染,留给可搜索下拉匹配用。- pt-select 按宿主的书写方向定位列表:RTL 下
align: start对齐到触发器右边缘(打开时读一次, 显式传给openFloating的dir)。新增内部工具utils/dom.ts的resolveDir()。 - 全局样式表的 FOUC 段补上
pt-option-group的 display。
- 新增子元素
a43f8a8:
pt-modal补齐 Dialog / AlertDialog 的能力:- 新属性
alert:确认框形态。面板role="alertdialog"、遮罩用--pt-bg-overlay-strong、标题与说明间距 8px、 默认不显示右上角关闭钮,点遮罩永远不关(close-on-overlay对它无效),Esc 仍然关。 不叫role:那是宿主元素的原生 ARIA 属性,同名 prop 会把宿主自己也变成一个 alertdialog。 hide-close改为「不设置时跟随形态」:普通弹窗显示、确认框隐藏;显式false可以让确认框也显示 ✕。 普通弹窗的行为不变。- 新属性
close-label:关闭钮的 aria-label,优先于内置文案t('modal.close')。 - 新属性
auto-focus(默认为真):为false时打开不移动焦点,焦点陷阱仍然生效 —— 焦点在弹窗外时按 Tab 会被拉进弹窗(点过遮罩空白处之后同样适用,以前这时 Tab 会跑到页面上)。 - 新事件
ptRequestClose(可取消,detail: { reason: 'close-button' | 'escape' | 'overlay' }): 用户关闭之前触发,preventDefault()则不关、也不发ptClose。hide()与改open不经过它。 - 导出类型
PtModalCloseReason/PtModalRequestCloseReason。 - 遮罩打开时进顶层(
showInTopLayer),不再受祖先transform/overflow/ 层叠上下文影响; 不支持 popover API 时退回原来的position: fixed+--pt-z-overlay。 - 面板在没有
aria-label时以 title 插槽为名(aria-labelledby),并以 description 插槽为说明(aria-describedby)。 - 输入法组合中按 Esc 不再关闭弹窗。
- 没有关闭钮时(
hide-close、确认框)标题不再给关闭钮预留右侧 32px。
- 新属性
1ff40f9:
pt-tooltip补齐:- 新增
align(start/center/end,默认center,反射)与offset(px,默认 4)。 - 默认间距由 6px 改为 4px,对齐源包 tooltip 的
sideOffset;气泡离触发元素近了 2px。 open可受控(mutable、反射):外部写入立即开关,不走delay;disabled时写true回落为false,显示期间被禁用立即收起。- 新增事件
ptOpen(气泡渲染出来后发,交互与属性打开都发)与ptClose({ reason: 'pointer' | 'blur' | 'escape' },只在用户交互收起时发),约定与pt-modal一致。 - 显示期间改
placement/align/offset都会按新参数重新定位(原来只有placement)。 - 触发元素里输入法组合中的 Esc 不再收起提示。
- 新增
0c2731f:
pt-empty-state按源包 Empty 补齐:- 新增
title插槽(有内容时替代heading文字)与description插槽(默认插槽继续作描述,两者可同时用)。 - 新增
mediaVariant(attributemedia-variant,default/icon,反射):icon把媒体位放进 40px 圆角底色方块、图标 24px。 - 新增 part
media/description/actions。 - 没有内容的插槽不再留空白(外壳隐藏,内容后加进来时自动显示)。
- 视觉对齐源包:1px 虚线边框(
--pt-border-default)、圆角 10px、内边距 24px(组件自身宽度 ≥ 768px 时 48px,容器查询)、 区块间距 24、标题 18/28 字重 500text-strong、描述 14/20text-standard(原来是 13/20text-weak),描述里的链接带下划线。 - 宿主改为
width: 100%的尺寸容器:放进居中的 flex 容器时也占满宽度。
- 新增
7acc1ed: 新增
pt-alert提示条(对应 design-components 的 Alert / AlertTitle / AlertDescription)。variant:'default'(默认)|'destructive',反射到宿主。destructive 下文字、图标、标题走--pt-text-danger, 边框为--pt-bg-danger-standard叠 50% 透明度。- 插槽
icon/title/ 默认(描述),都可选,空着的不占位。有图标时排成两列网格(16px 图标列 + 12px 间距), RTL 下图标自然落在右侧;插槽里的 svg 定成 16px。 - 宿主默认
role="alert",宿主上已写了role时不覆盖 —— 静态说明性提示条可写role="status"避免加载时被播报。 - part
base/icon/title/description。 - 全局样式表的 FOUC 段补上
pt-alert的 display。
61de759: 新增
pt-aspect-ratio宽高比容器(对应 design-components 的 AspectRatio)。ratio:宽 ÷ 高的数字,默认1,反射到宿主。写在宿主内联的aspect-ratio上;undefined(包装层没传)、NaN、0、负数、Infinity都按1处理,宿主高度不会塌成 0。- 宿主块级、宽度跟着容器;默认插槽的直接子元素拉满整个区域,默认
object-fit: cover, 子元素上自己写的object-fit优先。 - 比例严格成立:宿主
min-height: 0,内容比区域高时溢出而不是把区域撑高。 - 全局样式表的 FOUC 段补上
pt-aspect-ratio的 display。
11c0c40: 新增
pt-avatar头像(迁自 design-components 的 Avatar):- 属性:
src、alt、size(sm/md/lg= 24 / 32 / 40,默认lg,反射)、delay(毫秒,fallback 延迟显示,避免快速加载时闪一下首字母)。 - 默认插槽是 fallback 内容(首字母或图标),图片加载中 / 失败 / 未给
src时显示;src变化重置为加载中。 - 事件:
ptLoad/ptError,detail 为{ src }。 - 直接渲染
<img>,SSR 输出里就有图片;hydrate 前已加载完(或已失败)的在组件加载时补发事件。 - fallback 显示时宿主
role="img"+aria-label(取alt)。 - part
image/fallback,CSS 变量--pt-avatar-radius。
- 属性:
e00ce26: 新增
pt-button-group按钮组(对应 design-components 的 ButtonGroup)。orientation:'horizontal'(默认)|'vertical',反射到宿主;拼错或包装层传undefined时兜底回horizontal。- 宿主默认
role="group",使用方自己写了role(如toolbar)时不覆盖。 - 直接子元素里的
pt-button/pt-input/pt-select合并内侧圆角(逻辑属性,RTL 自动翻转), 两个控件直接相邻时后一个叠回 1px 让边框重合;悬停项z-index: 1、聚焦项z-index: 2,描边与焦点环不被邻居压住。 组里的pt-input吃掉多余宽度;组套组时子组之间留 8px;组里的pt-separator颜色换成--pt-border-input。 - 合并规则写在全局样式表
ptengine-ui.css里(影子树内写不出::slotted()::part()),依赖三个控件已开放的part="base"; FOUC 段同时补上pt-button-group的 display 与纵向的flex-direction。 - 源包的 ButtonGroupText / ButtonGroupSeparator 不单独做元素:分别用普通元素与纵向
pt-separator代替。 - Storybook 与文档站改为引入
ptengine-ui.css(此前只引了 tokens.css),示例里的合并效果才看得到。
2eff80c: 新增
pt-card卡片(对应 design-components 的 Card / CardHeader / CardTitle / CardDescription / CardContent / CardFooter)。- 插槽:
title、description、actions(标题行尾,RTL 下在左)、默认(内容)、footer。 - 空插槽不渲染也不留内边距:没有标题 / 说明 / 操作区时不渲染头部,没有页脚时不渲染页脚,只有内容时四周等宽留白。 首渲染前在
componentWillLoad检测一次,之后跟随slotchange与 MutationObserver(文本原地改写、slot 属性改写)。 - 外观在宿主上:
display: block、1px--pt-border-default边框、--pt-bg-box背景、--pt-text-standard正文色; 宿主不写overflow。标题 16px / 600 / leading-none / tracking-tight /--pt-text-strong,说明 14/20。 - CSS 变量
--pt-card-padding(默认 24px)、--pt-card-radius(默认--pt-radius-12); partbase/header/title/description/actions/content/footer。 - 全局样式表的 FOUC 段补上
pt-card的 display。
- 插槽:
18f1dff: 新增
pt-collapsible折叠面板(对应 design-components 的 Collapsible)。open(反射)+ptChange<boolean>受控值:ptChange只在用户点击触发元素切换时发,外部改open不发。 登记进openControls:Vue 支持v-model,Angular 支持[(ngModel)]。@ptengine/ui-angular新导出OpenValueAccessor(openControls 这组第一次非空); standalone 组件要在imports里显式加上它,ngModel才找得到 accessor。disabled(反射):点击不再切换,外部改open仍然生效;不改触发元素的外观。- 插槽:
trigger(触发元素,点击切换;键盘由触发元素自己的 click 覆盖)与默认插槽(内容)。 - 触发元素上维护
aria-expanded与aria-controls(指向默认插槽里的 light DOM 内容元素,缺 id 时自动补), slotchange 时新触发元素补上、旧的清掉。 - 内容区 part
content:grid-template-rows0fr ↔ 1fr 过渡,减少动态效果时关闭;收起时inert+ 过渡结束后visibility: hidden,Tab 进不去。 - 全局样式表的 FOUC 段补上
pt-collapsible,收起态在 upgrade 前先藏起内容。
01668b0: 新增
pt-kbd键盘按键与pt-kbd-group按键组合(对应 design-components 的 Kbd / KbdGroup)。- 两者都是光 DOM 元素(
shadow: false),无属性:宿主本身就是按键框 / 组合容器,子节点原样保留, 样式在全局样式表ptengine-ui.css,业务 className 可直接覆盖。 pt-kbd:行内 flex,高 20px、最小宽 20px、左右内边距 4px、圆角--pt-radius-sm、底色--pt-bg-secondary、 12/16 字号行高、medium 字重、--pt-text-weak;不响应指针、不可选中;内部 svg 渲染成 12px。不设role。pt-kbd-group:行内 flex,间距 4px。
- 两者都是光 DOM 元素(
6f797d8: 新增
pt-label标签文字(对应 design-components 的 Label)。- 14/20、中等字重、
--pt-text-strong;宿主display: inline-block,与原生 label 一样是行内级。 for(property 名htmlFor,for是 JS 保留字):点标签时在宿主所在的根(document 或外层影子树)里按 id 找目标。 勾选类 / 按钮类(pt-checkbox、pt-switch、pt-button、原生 checkbox / radio / button / file…)调用click(), 其余(pt-input、pt-textarea、pt-select、原生文本框……)调用focus()。点在标签内的链接 / 按钮上、 目标就包在标签里、disabled时不转交。双击标签不选中文字。required:尾部危险色星号,aria-hidden;必填语义仍由目标控件的required表达。disabled:半透明 +not-allowed光标,点击不再转交。- part
base/required。 - 它不会成为控件的可访问名(原生 label 关联跨不过影子边界、宿主上的
aria-labelledby也到不了影子树里的控件), 文档写明由控件自己的aria-label/ 插槽文字给名字。 - 全局样式表的 FOUC 段补上
pt-label的 display。
pt-checkbox/pt-switch:宿主自己被click()时也会切换并发ptChange,与原生input[type=checkbox].click()一致 (pt-label 靠它转发)。影子树与插槽文字上的真实点击仍只切换一次。- 14/20、中等字重、
86a5f45: 新增
pt-progress进度条(对应 design-components 的 Progress)。value:0 到max,缺省 /null/NaN按 0,超出范围夹紧;max默认 100,缺省、非数或<= 0时兜底 100。两者都反射到宿主。- 宿主
role="progressbar",aria-valuemin="0"、aria-valuemax/aria-valuenow取兜底夹紧后的值; 支持aria-label(包装层里属性名ariaLabelText),写在宿主上。 - 指示条用
inset-inline-start+ 百分比宽度,RTL 下从右往左长;宽度过渡走--pt-duration-fast,减少动态效果时关闭。 - CSS 变量
--pt-progress-height(默认 8px);宿主占满宽度,也可直接给元素写height。partbase/indicator。 - 全局样式表的 FOUC 段补上
pt-progress的 display 与尺寸。
6133454: 新增
pt-separator分隔线(对应 design-components 的 Separator)。orientation:'horizontal'(默认)|'vertical',反射到宿主。横向占满容器宽度(扣除自身外边距); 纵向宽 1px,在 flex 行里撑满行高,在普通行内默认 1em 高。decorative:默认true,宿主role="none";设为false时宿主role="separator",纵向再带aria-orientation="vertical"。- 默认插槽有内容时渲染成「线 文字 线」(只对横向生效)。
- CSS 变量
--pt-separator-color(默认--pt-border-default);partline/label。 - 宿主不带外边距,间距交给容器(如
::slotted(pt-separator))或使用方。 - 全局样式表的 FOUC 段补上
pt-separator的 display。
3cee3ef: 新增
pt-skeleton骨架屏(对应 design-components 的 Skeleton)。- 无属性:尺寸完全由使用方写在宿主上,占位直接画在宿主上(块级,默认圆角
--pt-radius-md,底色--pt-bg-secondary)。 - CSS 变量
--pt-skeleton-radius:圆形(头像)占位设为9999px。 - 宿主
aria-hidden="true";「正在加载」由使用方在加载容器上用aria-busy表达。 - 透明度脉冲动画,
prefers-reduced-motion: reduce下停住。 - 全局样式表的 FOUC 段补上
pt-skeleton的 display。
- 无属性:尺寸完全由使用方写在宿主上,占位直接画在宿主上(块级,默认圆角
51613e1: 新增
pt-table表格(对应 design-components 的 Table)。光 DOM 元素(shadow: false),里面直接写原生<table>,单元格不用加 class;默认样式在全局样式表里,选择器全部包:where(),业务 className 一写就能盖过。stickyHeader(attributesticky-header):表头吸顶,默认开,不反射。关掉时宿主写data-sticky-header="false";原生 HTML 的sticky-header="false"样式表直接认,JS 到达前首帧就是对的。 宿主自己是滚动容器,给它一个高度上限吸顶才生效。bordered:列间竖线,默认false,反射。- 视觉对齐源包:外框 1px + 8px 圆角改为默认(原全局段只在
bordered时有);字号 13 / 20(原为 14 / 20); 表头 40px 高、--pt-bg-default不透明底 + inset 底线(原为--pt-bg-secondary底);tr[data-state="selected"]选中底色;tfoot半透明 weak 底 + 500;caption在下方、13 / 20 弱化色、上间距 16; 含pt-checkbox的单元格尾部内边距为 0。 - 表格改用
border-collapse: separate+ 0 间距:边线属于单元格,吸顶时随表头一起吸住。 - 宿主可聚焦时(使用方给
tabindex="0")有与控件一致的焦点环。 - render 不带
<slot />(带了浏览器端会往宿主里插节点,React / Vue hydration 报 mismatch); 服务端渲染时撤掉 Stencil 给子元素打的hidden,首屏 HTML 里表格可见,使用方自己写的hidden保留。
52e025e: 新增 pt-popover 气泡卡片(批 2 · C2.1):
- 属性
open(可写、反射)、placement(默认bottom)、align(默认center)、offset(默认 4)、modal(焦点关在浮层里)、auto-focus(默认开:聚焦内容里第一个可聚焦元素,没有就聚焦浮层本身)、aria-label(ariaLabelText)。 - 插槽
trigger(点击切换,组件维护aria-expanded/aria-haspopup="dialog"/aria-controls)、anchor(可选的定位锚点)、默认插槽为内容。 - 事件
ptOpen、ptRequestClose(可取消,{ reason: 'escape' | 'outside' | 'trigger' })、ptClose({ reason },外部改open不发)。 - 浮层进顶层,不被
overflow: hidden祖先裁切;窄屏(≤640px)切底部抽屉。新 CSS 变量--pt-popover-width(默认 288px)。
弹层驱散工具与层栈:
- 新增
utils/popup.ts的dismissable():非模态弹层(popover,以及后续的 menu / hover-card)共用的 Esc 与外部点击处理, 与 pt-modal 共用层栈,只有最上层响应 Esc;已被preventDefault的 Esc 跳过。 - pt-modal:已被
preventDefault的 Esc 不再关闭弹窗 —— 弹窗里展开着的 pt-select、有内容的搜索框按 Esc 时只收起下拉 / 清空搜索词。 - 弹层定位(utils/floating 的 computePosition)改按布局尺寸量弹层,不再受入场动画 transform 影响: 带缩放入场的弹层朝上翻转时不再压到锚点上,居中对齐不再偏移。
- 属性
c766ef3: 新增标签页
pt-tabs+pt-tab+pt-tab-panel(批 2 · C2.2):pt-tabs:value(受控,已登记textControls,Vuev-model/ AngularngModel可用)、variantsegmented(默认)/line、sizesm/md/lg、activation-modeautomatic(默认)/manual、aria-label。ptChange只在用户切换时发,且不冒泡(标签页会嵌套,理由同pt-collapsible)。value缺省时显示第一个未禁用的标签。pt-tab:value、disabled;自己挂到标签栏插槽,宿主role="tab",aria-selected/aria-controls/ rovingtabindex由pt-tabs维护。pt-tab-panel:value,宿主role="tabpanel"、aria-labelledby,未选中hidden。- 键盘按 WAI-ARIA Tabs:←/→ 漫游(RTL 对调、首尾循环、跳过禁用)、Home / End;
manual模式 Enter / Space 选中。 - 只认自己的直接子元素,嵌套的标签页互不串扰。
a756cb2: 新增
pt-radio-group单选组与子元素pt-radio(对应 design-components 的 RadioGroup / RadioGroupItem,批 2 · C2.3)。pt-radio-group:value(反射,空串 = 未选)+ptChange<string>受控值,只在用户点选 / 方向键 / Space 时发, 外部改value不发。登记进textControls:Vue 支持v-model,Angular 支持[(ngModel)]。 其余属性name/orientation(vertical默认 |horizontal,反射)/disabled/required/invalid/aria-label。- 表单参与:form-associated,按
name提交选中值,未选提交null(字段不出现在 FormData 里),required未选报 valueMissing,invalid报 customError;表单重置回初始值,<fieldset disabled>让整组禁用。 - 无障碍:宿主
role="radiogroup"+aria-orientation/aria-required/aria-invalid/aria-disabled; 每项影子树里的圆点role="radio"+aria-checked/aria-disabled,可访问名经aria-labelledby取插槽文字; role 与 Tab 停靠点不放在包着插槽的宿主上,标签文字里的链接、按钮对读屏与 Tab 仍然可用。 - 键盘(WAI-ARIA Radio Group):roving tabindex,Tab 只停在选中项(无选中时第一个可用项);上下左右方向键移动即选中、 到头回绕、跳过禁用项,RTL 下左右对调;Space 选中当前项。
pt-radio:value(必填、非空,与 Radix RadioGroupItem 一致;缺了这一项不可选、方向键跳过,不回退到标签文字)/disabled/aria-label(插槽里没有文字时给名称,如配合pt-label的for);checked由组维护。整行(圆点 + 文字)都是点击区, 标签文字里的链接 / 按钮点了不选中;宿主click()等同于点它。part:base/control/indicator/label。pt-label的for支持指向pt-radio(点标签即选中)。
261b5b0: 新增
pt-accordion手风琴与子元素pt-accordion-item(批 2 · C2.4,对应 design-components 的 Accordion)。pt-accordion:type(single/multiple,默认 single)、value(single 为字符串,multiple 为字符串数组; HTML attribute 下 multiple 用逗号分隔)、collapsible(single 下再点已开项可收起)、disabled。 登记进textControls:Vue 支持v-model,Angular 用TextValueAccessor支持[(ngModel)],数组值原样穿过。ptChange只在用户点击标题切换时发,detail 是切换后的value;不冒泡(手风琴会嵌套,同pt-collapsible)。- 展开态由
value回写到直接子项的open(反射);只认直接子项,嵌套的内外层互不串。 pt-accordion-item:value、disabled、heading-level(默认 3);插槽trigger(标题)与默认插槽(内容); 影子树按 WAI-ARIA Accordion 渲染role="heading"><button aria-expanded aria-controls>+ 旋转的 ChevronDown, 内容区role="region"。part:heading/trigger/icon/content/body。- 键盘:每个标题都在 Tab 序列里,Enter / Space 切换;↑ ↓(回绕)/ Home / End 在各项标题间移动,禁用项跳过。
utils/roving.ts新增两个选项:manageTabIndex: false(只移动焦点、不写 tabindex)与focusTarget(项是宿主、真正聚焦影子树里的按钮)。- 展开 / 收起的内容区样式从
pt-collapsible抽成utils/disclosure.css共用,collapsible 行为与外观不变。 - 全局样式表的 FOUC 段补上
pt-accordion-item,upgrade 前先藏起内容。 - 用户第一次在手风琴里切换之前,展开态变化(初始化、框架水合后写入的 value)不播过渡,直接切到位。
dd0b531: 新增下拉菜单 pt-menu 全家(批 2 · C2.5):
- pt-menu:
open(可写、反射)、placement(默认bottom)、align(默认center,源包未设、取 Radix 默认)、offset(默认 4)、aria-label(ariaLabelText);插槽trigger/ 默认。触发元素上维护aria-haspopup="menu"/aria-expanded(支持 ARIA 元素反射的浏览器里经ariaControlsElements指向面板)。点击打开聚焦面板;触发元素上 ↓ / Enter / Space 打开并聚焦首项、↑ 聚焦末项。 事件ptOpen、ptClose{ reason: 'select' | 'escape' | 'outside' | 'trigger' | 'tab' }(外部改open不发)。 Tab 关闭菜单、焦点照常往后走。面板进顶层,窄屏不切抽屉。 - pt-menu-item:
type(default/checkbox/radio)、value、checked(可写、反射)、disabled、inset; 插槽start(图标)/ 默认 /end(快捷键)。ptSelect(冒泡、可取消,preventDefault()后菜单不关); 勾选项另发ptChange<boolean>(不冒泡,避免与 pt-menu-group 的同名事件串)。 - pt-menu-group:
type(default/radio)、value(可写)、label;type="radio"时管理组内单选项并发ptChange<string>(不冒泡,组可经子菜单嵌套)。登记 textControls:Vuev-model、Angular[(ngModel)]可用。 - pt-menu-label:不可选的小标题,
inset。 - pt-menu-sub:子菜单。
open(由菜单维护)、disabled、inset;插槽trigger/ 默认。悬停约 100ms、点击、→ / Enter / Space 打开, ← / Esc 只关子菜单并回到触发行(RTL 对调);离开后约 300ms 宽限再关(不做安全三角形)。 - 新工具
utils/menu-controller.ts(菜单面板的开关、驱散、漫游 + typeahead、项激活、子菜单开关,给后续 pt-context-menu 复用)、utils/menu-events.ts、utils/menu-panel.css。
- pt-menu:
90b7a35: 新增
pt-hover-card悬停卡片(批 2 · C2.6,对应 design-components 的 HoverCard)。- 属性
open(可写、反射)、open-delay(默认 700ms)、close-delay(默认 300ms)、placement(默认bottom)、align(默认center)、offset(默认 4);插槽trigger与默认插槽(卡片内容);partbase/panel。 - 指针进入触发元素按
open-delay打开,离开后按close-delay关闭,期间移进卡片就取消关闭;触屏的指针进出不理会。 键盘聚焦触发元素(:focus-visible,鼠标点击聚焦不算)同样打开,焦点离开触发元素与卡片即关闭;Esc 与外部按下走utils/popup的层栈。 - 事件
ptOpen、ptClose({ reason: 'pointer' | 'blur' | 'escape' | 'outside' },外部改open不发)。 - 与 Radix HoverCard 同一语义:只是视觉增强,不设
role="dialog"、触发元素不写aria-expanded;文档写明只放补充信息。 窄屏不切抽屉。新 CSS 变量--pt-hover-card-width(默认 256px),其余外观与pt-popover相同。 - 卡片型浮层面板的外观与入场动画从
pt-popover抽成utils/popup-panel.css共用,popover 外观不变。
- 属性
f25e234: 新增
pt-toggle切换按钮(批 2 · C2.7,对应 design-components 的 Toggle)。- 属性:
pressed(受控、反射)、variant(default/outline)、size(sm/md/lg,默认 md)、disabled、aria-label(包装层里是ariaLabelText,纯图标时必须给)。 - 影子树是原生
<button part="base" type="button" aria-pressed>,delegatesFocus;Space / Enter 都切换。 ptChange<boolean>:用户点击切换后发,detail 是切换后的pressed;外部改写pressed不发。 作为 pt-toggle-group 的直接子元素时,点击不自行切换、也不发 ptChange(为 C2.8 预留,按父元素 tagName 判断)。pressedControls第一个成员:Vue 支持v-model(绑pressed);Angular 新增导出PressedValueAccessor, standalone 组件 import 它后[(ngModel)]绑pressed(只认宿主自己发出的 ptChange)。- 视觉对齐源包 toggleVariants:状态层、按下态
bg-selected+ 最强文字色、outline 1pxborder-input; md 32 高 / min-w 36,sm 24 / 32,lg 40 / 40(图标 20px);高度与最小宽度写在宿主上(控件宿主约定)。 定制点--pt-toggle-radius。 - 全局样式表的 FOUC 段补上
pt-toggle(inline-flex)。
- 属性:
495a65b: 新增
pt-toggle-group切换按钮组(批 2 · C2.8,对应 design-components 的 ToggleGroup);pt-toggle新增value。- 属性:
type(single/multiple,默认 single)、value(受控;single 为字符串、multiple 为字符串数组, HTML attribute 写法同 pt-accordion 用逗号分隔)、variant(default/outline)、size(sm/md/lg)、disabled、orientation(horizontal/vertical,默认 horizontal)、loop(默认 true)、aria-label。 - 按
value把按下态回写到直接子元素pt-toggle的pressed(写 attribute,子元素未升级也生效); 组上写了variant/size时回写到每一项(组级优先,项上之后再改也会被改回去;两者无默认值,组上不写时各项用自己的);组的disabled禁用全部项但不改写项自己的disabled。 ptChange(冒泡)只在用户点击时发:single 下为字符串,再点已按下的项取消选择、detail 为''(同 Radix); multiple 下为新数组。登记textControls:Vuev-model与 AngularngModel(TextValueAccessor)对字符串和数组都工作。- 键盘:roving tabindex(整组一个 Tab 停靠点,停在第一个按下的项、否则第一个可用项),方向键按
orientation只移焦点不改按下态,RTL 下左右对调,Home / End,跳过禁用项,loop控制回绕;Space / Enter 由项内原生按钮触发。 - 无障碍:single / multiple 下组都是
role="group"、项内按钮都是切换按钮(aria-pressed)。single 不用 radiogroup / radio(Radix 用了):可取消选择、方向键只移焦点不选中,与 WAI-ARIA 单选组的交互约定不符。 - 视觉对齐源包 toggle-group.tsx:flex、居中、gap 4;纵向时竖排、各项等宽。宿主
inline-flex(源包为块级 flex), FOUC 段补上pt-toggle-group。 pt-toggle:新增value(反射,只在组内有意义);直接父元素是 pt-toggle-group 时组的disabled也禁用它 (property 优先、组未升级时读 attribute);value/disabled/pressed变化时通知组refresh()—— 外部直接改组内项的pressed会被组按value覆盖回去(不发ptChange),组始终是按下态的唯一来源。
- 属性:
9382faa: 新增右键菜单 pt-context-menu(批 2 · C2.9),并让
ptSelect可以在菜单上统一监听:- pt-context-menu:
open(可写、反射;外部打开时从区域中心弹出)、disabled、aria-label(ariaLabelText); 插槽trigger(右键区域)/ 默认(菜单内容,复用 pt-menu-item / group / label / sub 与 pt-separator)。 区域上右键(拦下浏览器菜单)在指针处打开,面板左上角贴着指针、右下展开,贴边翻转 / 收进视口;开着时在区域别处右键先关再在新位置重开。 触屏长按 700ms 打开(移动超过约 10px 取消,打开后吞掉随后的 click / contextmenu);区域里按 Shift+F10 / ContextMenu 键从区域中心打开并聚焦首项。 不改区域的 tabindex。事件ptOpen、ptClose{ reason }(与 pt-menu 同一组)、ptSelect。宿主display: contents。 视觉差异照源包 ContextMenu:分隔线border-default、快捷键text-weak(经内部变量传给 pt-menu-* 子元素,pt-menu 下不变)。 - pt-menu / pt-context-menu 声明
ptSelect(cancelable,detail 同菜单项):组件自己不发,只为让 React / Vue / Angular 包装层 生成绑定 ——<PtMenu onPtSelect>、<PtMenu @pt-select>现在收得到菜单项冒泡上来的选择,每次只收到一次, 在菜单上preventDefault()同样阻止关闭。以前写在各个菜单项上的用法照旧可用。 - 菜单面板限高到视口里(pt-menu / pt-context-menu / pt-menu-sub 共用):面板高度按所选方位的可用空间限高 (
utils/floating新增fitHeight选项,写内联max-height,关闭时还原),从视口中部打开的长菜单不再伸出视口、 首项 / 末项够不着,而是整块留在视口里内部滚动;可用空间不足两行时翻到空间大的一侧。
- pt-context-menu:
cd4ac31: 跨影子边界的标签关联(批 3 · C3.0):
pt-label的for现在会把标签文字交给目标控件,成为它的可访问名。- pt-input / pt-textarea / pt-select / pt-checkbox / pt-switch / pt-radio / pt-radio-group 接入:名字落在各自影子树里 真正获得焦点的元素上(radio-group 落在宿主的 ElementInternals 上),控件上不必再写与标签同文案的
aria-label。 外部标签优先于控件自己的aria-label与插槽文字;标签移除后退回原来的名字。 - 标签晚于 / 早于控件出现、控件换 id、
for改指、标签移除、标签文字变化,关联都跟着更新(同一个根里的所有标签共用一个 MutationObserver)。 - 机制:支持 ARIA 元素反射的浏览器(Chrome 135+、Firefox 136+、Safari 16.4+)写
ariaLabelledByElements/ariaDescribedByElements; 同时总把文字镜像进内部元素的aria-label/aria-description,作为 Chrome 114–134、Firefox 125–135 的退路,也让 axe、 PlaywrightgetByLabel等只认 id 属性的工具看得到名字。 - 新内部工具
utils/aria-link.ts(linkAria/AriaLinkController/watchIdRefs),支持 label / description / error 三类来源, 供后续 pt-field 使用;接入约定是控件宿主上的Symbol.for登记表,不增加任何公开属性或方法。 - 行为变化:pt-select 在有外部标签时,触发器的名字是标签文字,不再是当前显示的值;勾选类有外部标签时不再用插槽文字取名。
- pt-input / pt-textarea / pt-select / pt-checkbox / pt-switch / pt-radio / pt-radio-group 接入:名字落在各自影子树里 真正获得焦点的元素上(radio-group 落在宿主的 ElementInternals 上),控件上不必再写与标签同文案的
da80c15: 新增 pt-sheet 侧边面板(批 3 · C3.1):
- 属性
open(可写、反射)、side(top/right/bottom/left,默认right)、close-on-overlay(默认开, 与源包 / Radix 一致;关掉写close-on-overlay="false")、hide-close、close-label、auto-focus(默认开)、aria-label(ariaLabelText); 方法show()/hide()。 - 插槽
title、description、默认(正文)、footer(窄屏纵向、后写的在上,≥640px 横排靠行尾)。 - 事件
ptOpen、ptRequestClose(可取消,{ reason: 'escape' | 'overlay' | 'close-button' })、ptClose({ reason },多一种api; 外部改open不发),三者都不冒泡。 - 视觉照源包 sheet.tsx:遮罩
--pt-bg-overlay-strong,左右两侧宽 75%、≥640px 最多 384px(--pt-sheet-size可改),上下两侧宽度铺满; 打开 500ms / 关闭 300ms 从所在边滑入滑出、无缩放,减少动态效果时不播;右上角手绘关闭钮(16px 叉、opacity .7→1);标题 18/28。
新增共享模态层
utils/modal-layer.ts:层栈、滚动锁、焦点陷阱与归还、document 上的 Esc(只栈顶、跳过输入法组合与已被preventDefault的)、遮罩进顶层、可取消的ptRequestClose流程,从 pt-modal 抽出,pt-modal / pt-sheet 共用(行为不变)。 共享 chunk 因此 +1.3KB(29.96 / 32KB)。- 属性
340635a: 新增
pt-slider滑块(批 3 · C3.2,对应 design-components 的 Slider)。- 属性:
value(number[],单值也用数组,滑块数 = 长度;HTML attribute 写逗号分隔,同 pt-accordion / pt-toggle-group)、min(0)max(100)step(1)、orientation(horizontal/vertical,竖向下端为最小值)、disabled、name、min-steps-between-thumbs(0)、aria-label;只能 JS 赋值的thumbLabels(多滑块各自的读屏名)与formatValue(value, index)(写aria-valuetext)。源包只有一档尺寸,不加size。 - 取值规整:缺省 /
null/ 空串 / 空数组按[min](AngularngModel初值 null、Vue 初值 undefined 都能直接用); 外部值按 min / max 夹紧、按 step 对齐、排序后用于显示、读屏与表单,不回写value,用户交互时才写回规整过的新数组。 - 事件:
ptChange<number[]>在值每次变化时发(拖动中持续发,冒泡:滑块不自我嵌套);新增ptCommit<number[]>, 一次操作结束且值变了时发(指针松开,或每一次改变了值的按键)。两者只在用户交互时触发。登记textControls, Vuev-model与 AngularngModel对数组生效。 - 交互(Radix Slider):指针捕获拖动,按在滑块上不跳;点轨道把最近的滑块移过去并接着拖,重叠的滑块按第一次移动方向挑;
touch-action: none。键盘 ← ↓ / → ↑ 一步(横向 RTL 左右对调)、PageUp / PageDown 与 Shift + 方向键十步、Home / End; 多个滑块不交叉,相邻至少隔minStepsBetweenThumbs步。 - 无障碍:每个滑块是影子树里的
role="slider"(aria-valuenow/min/max、aria-orientation、tabindex=0); 单滑块时aria-label给滑块,多滑块时宿主role="group"带aria-label,滑块默认念「最小值 / 最大值」 (三个及以上按序号),i18n 新增slider.minimum/slider.maximum/slider.thumb。 - 表单:formAssociated,按同一个
name每个值提交一条;表单重置回到初始值,<fieldset disabled>生效。utils/form.ts的setFormValue/syncFormControl接受FormData值,新增multiFormValue(name, values)。 - 视觉照源包 slider.tsx:轨道 6px 全圆角 bg-secondary、已填充段 bg-strong、滑块 16px 圆 1px border-neutral bg-default、 focus-visible 1px 焦点环;禁用只让已填充段 opacity .3 + not-allowed 指针,滑块不变(源包实测结论)。FOUC 段补上
pt-slider。
- 属性:
3e88d1b: 新增原生下拉 pt-native-select(批 3 · C3.3):
- 影子树里是真正的原生
<select>,选项沿用pt-option(value/disabled/ 文本)与pt-option-group(label→<optgroup>), 由组件镜像成原生<option>/<optgroup>;增删、改文本、改 value / disabled / label 经 MutationObserver 跟上。pt-option/pt-option-group在这里只是数据、不渲染自身(全局样式含 upgrade 前的display: none)。 - 属性
value(受控,已登记textControls,Vuev-model/ AngularngModel可用)、name、disabled、required、invalid、sizesm/md/lg、placeholder(渲染value=""、disabled、hidden 的首项)、aria-label(ariaLabelText); 事件ptChange(原生 change 时发,冒泡)。form-associated:提交原生控件实际显示的值,占位项时required报 valueMissing, 支持表单重置、fieldset 禁用与状态恢复。 - 值不在选项里时与原生一致:有
placeholder显示占位项,否则显示第一个可选项;value属性本身不被改写。 - 视觉照源包:高 32(sm 24 / lg 40)、圆角 8、1px border-input、内边距 12 / 32、13/20 strong、悬停 border-neutral、 focus-visible 1px 焦点环、invalid 红边与 30% 红色焦点环、禁用 .5;ChevronDown 16 weak 距末端 10。 CSS 变量
--pt-native-select-radius,partbase/select/icon。
@ptengine/tokens:tokens.css 在明暗主题作用域里新增--pt-color-scheme(light/dark,跟随系统时在媒体查询里切换)。 它不是颜色、不进 tokens.json;组件用color-scheme: var(--pt-color-scheme, normal)让 UA 原生部件(原生下拉列表等)跟随主题明暗, 局部主题孤岛也按最近的祖先继承。utils/form.ts的syncFormControl的native选项接受HTMLSelectElement。- 影子树里是真正的原生
a917f88: 新增表单项 pt-field、字段组 pt-field-set、表单项分组 pt-field-group(批 3 · C3.4):
- pt-field:插槽
label/ 默认(控件)/description/error;属性orientation(vertical/horizontal/responsive,≥768px 转行)、invalid、required、disabled、errors: string[](只走 property;去空去重,一条纯文本、 多条列表,没有内容不渲染,role="alert";error插槽有内容时以插槽为准)。 默认插槽里第一个 kit 表单控件(可包在容器里,不进嵌套的 pt-field)经utils/aria-link拿到标签作可访问名、 说明与错误作描述;invalid/required/disabled以 attribute 下发给它,撤回时只移除自己加的。控件后插入、替换、移除, 标签改了slot或被移除时关联跟着更新并解除旧的。点标签聚焦控件,pt-checkbox/pt-switch切换。 排列顺序跟随子元素的 DOM 先后,横向时控件写在标签前就排左侧。 视觉照源包:间距 8;标签 14 中等字重 strong、行高 snug;说明 14/20 standard;错误 14/20 danger; 横向时标签列 gap 6、垂直居中。partbase/content/label/required/description/control/error。 - pt-field-set:
role="group"+aria-labelledby指向legend插槽;legend-variantlegend(16/24)/label(14/20), 中等字重 standard、下边距 12;组内纵向间距 24。disabled像原生<fieldset disabled>一样下发给组内的 pt-field、 kit 控件、pt-button与原生表单控件 / 按钮(含禁用期间新加入的),撤回时只移除自己加的。partbase/legend/content。 - pt-field-group:纵向排列、间距 28,只管排版。FieldSeparator 不单做,用带文字的
pt-separator。 - 新增
utils/field-control.ts:两者共用的控件清单与按来源计数的布尔 attribute 下发(Symbol.for登记表,多来源并集)。
体积预算:懒加载入口一次加三个标签后实测 2.85 KB,按「每批按实测再调、留约 3 个组件余量」由 2.8 调到 3; 共享 chunk 因
utils/field-control(pt-field 与 pt-field-set 共用,约 0.4 KB)实测 32.22 KB,由 32 调到 33。- pt-field:插槽
29fa641: 新增输入组 pt-input-group(批 3 · C3.5):
- 插槽
start/ 默认 /end:默认插槽放一个pt-input(含type="search"搜索形态)或pt-textarea(多行,宿主标data-multiline、高度随内容、前后缀贴顶); 前后缀放文字 / 图标(14/20 弱色,svg 16px),末尾按钮用pt-button size="sm" variant="ghost"(源包 InputGroupButton)。 - 宿主画框(
part="base"):高 24 / 32 / 40、圆角 8(sm 为 6)、1px border-input、内边距 12(sm 8 / lg 16)、间距 8、悬停 border-neutral、 焦点在输入框里时 1px 焦点环(data-focused)、出错红边与 30% 红色焦点环、禁用时外框与前后缀 .5 + not-allowed。 CSS 变量--pt-input-group-radius。宿主默认role="group"(已有 role 不覆盖)。 size由输入组下发:写成子pt-input的sizeattribute(未升级也生效)。invalid/disabled以子控件为准(含 fieldset 禁用、pt-field 下发), 输入组经 MutationObserver 读取并反射成宿主data-invalid/data-disabled。- 点在前后缀的文字、图标或外框留白上聚焦输入框(mousedown 拦默认行为:已聚焦时不会先失焦);按钮、链接等可交互元素不插手。 触屏(粗指针)下命中区上下扩到至少 44px,视觉高度不变。
pt-input 新增 CSS 变量
--pt-input-padding-inline、--pt-input-focus-ring;pt-textarea 新增--pt-textarea-border-width、--pt-textarea-padding-inline、--pt-textarea-focus-ring。默认值与原来一致,输入组用它们去掉子控件自己的描边、内边距与焦点环,不穿透影子树。修正 pt-input:
prefix/suffix空着时不再占一个弹性项。原来空前缀也吃一份 8px 间距,文字离左边框是 20, 与源包的 12 不符;放进输入组后前缀文字与输入文字之间也会多出 8。- 插槽
d027a3e: 新增可搜索下拉 pt-combobox(批 3 · C3.6):
- 触发框本身就是输入框,打字即过滤
pt-option(可混放pt-option-group/pt-separator):文案与keywords(空格分隔) 任一包含查询即命中(trim、不分大小写),保持原顺序、禁用项照样可见;组内全被滤掉时整组藏起、有查询时分隔线藏起; 没有匹配项时显示empty-text(不给时取 locale 的combobox.empty)。收起时清空查询。 value区分「未选」(undefined,null视同)与「选了空串」('',合法的已选值):前者显示占位、表单不提交、required报必填; 后者可以高亮、选中,提交空串。已登记textControls:Vuev-model/ AngularngModel/ Reactvalue+onPtChange都保留这一区分。- 属性
placeholderempty-textmax-height(列表最大高度,默认 320)namesize(sm / md / lg)disabledinvalidrequiredopenaria-label(ariaLabelText);方法setFocus/show/hide/refresh;事件ptChange(冒泡)、ptOpen/ptClose(不冒泡, reason 与 pt-select 同形)、ptFocus/ptBlur。form-associated,支持表单重置与 fieldset 禁用。接入 aria-link(pt-label for/ pt-field)。 - 源包「删了就坏」10 条逐条翻译成 kit 语义并各有一条 spec:Home / End 留给光标;收起时 Enter 放行并对所属表单 requestSubmit(隐式提交);
''值可高亮可选;重复 value 按行区分;展开时再点输入框不收起;面板里只在选项上 mousedown preventDefault(滚动条可拖); 打开 / 关闭不抢焦点、不全选文字;展开中被禁用即收起并清查询;选中前再判禁用;输入法组合中不响应 Enter / 方向键。 - 视觉照源包:触发框与 pt-select 同一套(高 32、圆角 8、border-input、13/20);已选文案在输入框下一层、打字时 opacity 0 占位不跳宽; 状态图标展开 Search / 收起 ChevronDown .5;面板 openFloating bottom start offset 4、按可用高度限高(fitHeight 80),min-width = 触发框宽(不超过视口减 16px)、max-width
min(420px, 90vw)(只限按内容撑宽), padding 4,复用 popup-panel.css。触屏命中区 44px。CSS 变量--pt-combobox-radius,partbase/input/value/popup/listbox/empty。
新增
utils/filter.ts:过滤规则(normalizeQuery/splitKeywords/matchesQuery)与「输入框 + aria-activedescendant 列表」的状态 (FilterList:过滤写回hidden+data-filtered、只摘自己加的 hidden,高亮移动复用 listbox 的moveActive),供之后的 pt-command 复用。pt-option-group的选项增删同样通知所属的 pt-combobox;pt-label把 pt-combobox 列入可交互目标。@ptengine/ui-angular:TextValueAccessor对 pt-combobox 的writeValue(null / undefined)照原样写undefined,不再转成空串 (由 stencil.config 的nullableTextControls生成)。- 触发框本身就是输入框,打字即过滤
437fe7a: 新增 pt-drawer 抽屉(批 3 · C3.7,对应源包 drawer.tsx / vaul):
- 属性
open(可写、反射)、direction(bottom默认 /top/left/right,物理方向,RTL 下不翻转,与 vaul 一致)、dismissible(默认开;关掉后遮罩、Esc 不关,拖动只回弹)、handle(默认开,上下方向显示 100×8 把手)、snapPoints(只走 property:0–1 比例或'240px',缺省只有全开)、snap-index(可写、反射)、auto-focus、aria-label(ariaLabelText); 方法show()/hide()。 - 插槽
title、description、默认(正文,在面板里滚动)、footer(纵向排列);正文或页脚里带data-drawer-close的元素点击即关闭(对应DrawerClose)。 - 事件
ptOpen、ptRequestClose(可取消,{ reason: 'escape' | 'overlay' | 'drag' | 'close' },拖动被拦下时回弹)、ptClose({ reason },多一种api;外部改open不发)、ptSnap({ index, point },用户拖到新吸附点时),四者都不冒泡。 - 拖动:pointer capture;只在把手 / 面板上不可滚动处起拖,正文里可滚动区域没滚到顶之前往下拉是滚动;文本框、下拉框、
data-drawer-no-drag与 pointerdown 已被 preventDefault 的控件(如 pt-slider)上不起拖。松手按 vaul 的阈值判定 (0.4 px/ms、露出部分 25%、2 px/ms 快速甩动逐档 / 到底),遮罩随拖动淡出。判定逻辑是utils/drawer-gesture.ts的纯函数。 - 模态层复用
utils/modal-layer(层栈、滚动锁、焦点陷阱与归还、遮罩进顶层、重插恢复)。 - 视觉照源包:遮罩
--pt-bg-overlay-strong,面板 bg-default、1px 边、贴边一侧圆角 10、对边至少留 96px, header grid gap 6 p16,footer mt auto column gap 8 p16,标题 18/28 600 tracking -0.025em;开合 500mscubic-bezier(0.32, 0.72, 0, 1), 减少动态效果时不播。shouldScaleBackground不做。
体积:pt-drawer 5.46KB(中档 8KB);拖动判定放在组件自己的入口,共享 chunk 不变(31.83 / 32KB)。 懒加载入口 45 个组件实测 2.83KB,超过 2.8,按「每批按实测、留约 3 个组件余量」上调到 3。
- 属性
bc5f5ad: 新增命令面板 pt-command + pt-command-group + pt-command-item(批 3 · C3.8):
- 顶上一个搜索框(Search 16 weak、高 40、底边),下面是可过滤的命令列表(max-height 300 内部滚动)。项写成
pt-command-item(valuekeywordsdisabled,slotstart图标 / 默认文案 /end快捷键 13/20 weak 0.1em),可以用pt-command-group(label组标题,13/20 500)分组、夹pt-separator(margin 0 -4)。 - 与 pt-combobox 共用
utils/filter:文案(默认插槽的文字,不含 start / end)与keywords任一包含查询即命中, 保持原顺序、不按匹配度重排(有意偏离 cmdk 的打分排序,文档注明);禁用项照样可见;空组整组藏起、有查询时分隔线藏起; 没有匹配时显示empty-text(缺省 locale 的command.empty)或empty插槽。filter属性可传自定义函数,should-filter="false"关掉内置过滤(异步搜索)。项的增删与文案 / value / keywords / disabled 变化由 MutationObserver 自动重新过滤。 - 焦点始终在输入框上(
role="combobox"+aria-activedescendant→role="listbox"):↑↓ 移动高亮、跳过禁用项,loop(默认 false,同 cmdk); Home / End 到首尾(同 cmdk);Enter 选中;指针移过即高亮;输入法组合中不响应。Esc 不处理,留给外层弹窗。 ptSelect(cancelable,由项发出、冒泡):pt-command 声明了同名事件,包装层能在面板上绑定(同 pt-menu)。没被取消时面板随后清空查询、 高亮回到第一项(等同「重新打开」:放进弹窗时下次打开不停在上次的结果上)。value是高亮项(不是选中值),不登记受控清单;query可读写。两者因用户操作改变时分别发ptValueChange/ptQueryChange,外部写入不发。label(输入框与列表的无障碍名称,缺省 locale 的新键command.label),接入 aria-link(pt-label for)。- CommandDialog 不单做:文档给
pt-modal包pt-command的写法,源包的尺寸差异经 CSS 变量覆盖:--pt-command-input-height(48)、--pt-command-item-padding-block(12)、--pt-command-icon-size(20),另有--pt-command-radius、--pt-command-list-max-height。
utils/filter.ts的FilterList新增match选项(自定义命中判断,拿到原样查询与规范化后的查询)与可按函数现读的loop,补了 spec。 新增utils/command-item.ts(项文案提取与内部选中事件名,pt-command / pt-command-item 共用)。- 顶上一个搜索框(Search 16 weak、高 40、底边),下面是可过滤的命令列表(max-height 300 内部滚动)。项写成
9605123: 新增 pt-toaster 轻提示与
@ptengine/ui/toast子路径(批 4 · C4.1,对应源包 sonner.tsx / sonner 2.0):- pt-toaster:属性
position(六个位置,默认bottom-right,物理方向)、rich-colors、close-button、duration(4000)、visible-toasts(3)、expand、gap(14)、offset(默认 24px)、label、close-label; 方法show(options): id、update(id, options)、dismiss(id?)。每条的选项type(default / success / error / warning / info / loading)、title、description、action/cancel({ label, onClick },action 里 preventDefault 不关)、duration(0/Infinity常驻, 与 sonner 不同、沿用 AntD)、id(同 id 覆盖更新)、dismissible、closeButton、richColors、onDismiss、onAutoClose。 - 行为照 sonner:最新在前、后面缩小叠压(最多露出 3 条);悬停 / 聚焦 / 按住 / 页面隐藏时暂停计时并展开; 按住往贴边或水平方位方向滑动关闭(pointer capture,45px 或 0.11 px/ms,反方向带阻尼);Alt+T 聚焦通知区、Esc 还焦点; 宿主
aria-live="polite"、每条role="status"、error 那条aria-live="assertive";减少动态效果时不播过渡。 - 层级:通知区经
showInTopLayer进顶层;之后打开的弹层压在它上面时,出新通知会重新提到顶层最上面 (utils/floating新增hasLayerAbove,只在确实被压住时才进出,避免打断堆叠过渡)。宿主带aria-live,hideOthers设 inert 时跳过它。移出 DOM 时出顶层、暂停计时,插回后恢复。 - 视觉:bg-box、1px border-default、shadow-lg、圆角 8、宽 356、padding 16、标题 / 说明 13/20(源包未覆盖 sonner 的 13px, 计划里的「标题 14/20」以源包为准);语义色只染图标(danger / warning / success / information 的 standard 档);
rich-colors用语义 subtler 底 + 语义描边、文字不染;action 高强调黑、cancel 次要灰。 @ptengine/ui/toast:toast(message, opts)与.success / .error / .warning / .info / .loading / .message / .promise / .dismiss, 同步返回 id。找页面上的<pt-toaster>(优先使用方自己挂的),没有就在<body>末尾建一个;元素没注册时按需import('../components/pt-toaster.js'),注册前的调用排队、按顺序交付;Node(SSR)下调用是空操作。 Stencil 之外单独一步tsc -p tsconfig.toast.json产出dist/toast/,package.jsonexports 加./toast(turbo 的@ptengine/ui#buildoutputs 已含dist/**,不另登记)。三个包装包不再导出一份。- i18n 新增
toast.label(通知区读屏名称)。
体积:pt-toaster 进中档(8KB),实测见 MR;toast 子路径新增一条预算 3KB(实测 2.27)。
- pt-toaster:属性
9f05d0c: 新增
pt-breadcrumb面包屑与子元素pt-breadcrumb-item(对应 design-components 的 Breadcrumb*,批 4 · C4.2)。pt-breadcrumb:label(导航地标的读屏名,缺省走新增的内置文案breadcrumb.label:「面包屑」/ "Breadcrumb")、separator(文字分隔符,缺省画 14px 右向箭头、RTL 下翻转;空串只留间距)。宿主role="navigation", 影子树里只有一层role="list"的列表容器(part="list"):13/20、weak 色、flex 换行,间距 6px(视口 ≥640px 时 10px)。 CSS 变量--pt-breadcrumb-gap、--pt-breadcrumb-separator-color。pt-breadcrumb-item:光 DOM 元素(shadow: false),宿主role="listitem";current(aria-current="page",strong 色 + 500)、ellipsis(32×36 的「…」,读屏念label,缺省breadcrumb.more)。链接由使用方写在项里(<a>或框架的 Link 组件), 样式在全局样式表、选择器包:where(),业务写在项或<a>上的 className 直接盖过;触屏下链接本身撑到 44 × 44(命中区就是盒子,同行与换行后上下两行都不重叠)。- 分隔符与折叠图标都是伪元素(非首项的
::before/ 折叠项的::after),分隔符的替代文本为空、读屏不念; 光 DOM 里不渲染任何子节点,React / Vue 水合不会对不上。 - 与计划的偏离:项不提供
href(光 DOM 里把使用方文字包进组件自建的<a>会让水合报 mismatch,客户端路由也需要框架自己的 Link);pt-breadcrumb保留影子树,只为放列表语义(读屏念「第 N 项,共 M 项」需要项外面有一层列表,而宿主已是导航地标)。 - SSR:
pt-breadcrumb-item加进 React output target 的excludeServerSideRenderingFor(直接文本不被换成注释),@ptengine/ui-react/ssr补导出PtBreadcrumbItem。 - 体积:全局样式表 4.29 → 4.89 KB(预算 5,未顶破;两段 mask 图标的 data URI 占了大头)。
a7efec2: 新增
pt-pagination分页与子元素pt-pagination-item(批 4 · C4.3,对应 design-components 的 Pagination)。- 两个都是光 DOM 元素(
shadow: false):样式在全局样式表ptengine-ui.css,选择器全部包:where(), 业务在项上写 className、或用.biz > a写在里面的链接 / 按钮上都能直接盖过默认样式。 pt-pagination:role="navigation"地标,label缺省「分页」(新增内置文案pagination.label); 声明ptSelect(自己不发)以便三个框架包装层在分页上统一绑定项冒泡上来的事件。pt-pagination-item:kindpage / previous / next / ellipsis、page、href、active、disabled、label。 没有href渲染成按钮,点击发ptSelect(detail{ kind, page },冒泡);给了href渲染成链接、照常跳转。 内容全部由属性决定,不接收子节点。页码 32×32、上一页 / 下一页同高按文字撑宽,配色照 pt-button 的 ghost / secondary; 触屏下命中区 44px、项间距拉到 12px 使相邻命中区不重叠;RTL 下箭头翻转。- 不内置页码计算、不持有当前页(与源包一致),文档给出「当前页 ± 1 + 首末页」的写法。
- 服务端渲染:两个元素加进
excludeServerSideRenderingFor,@ptengine/ui-react/ssr导出客户端版本,首屏是空标签 (尺寸由全局样式表占住),链接在 upgrade 后渲染。@ptengine/ui-react的PtPaginationItem在生成的包装外加一层 (opaque-light-dom.ts),给宿主固定传dangerouslySetInnerHTML={{ __html: '' }}:项在自己的光 DOM 里渲染<a>/<button>, 而 Stencil 先于 Next.js 的 hydration 渲染,不加这层 React 比对子节点时必报 #418。 - 全局样式表预算 5 → 6KB:本段实测 +0.58(4.87),与并行的 breadcrumb 段合起来约 5.5,按批 1 预估的全局段上限定 6。
- 两个都是光 DOM 元素(
6f2cfd2: 新增列表项 pt-item + pt-item-group + pt-item-media(批 4 · C4.4):
- pt-item:源包 Item / ItemContent / ItemTitle / ItemDescription / ItemActions / ItemHeader / ItemFooter 合成一个元素, slot
mediatitledescription默认(说明下方的额外内容)actionsheaderfooter。variantdefault / outline / muted (muted 走 bg-secondary),sizemd(gap 16、p 16)/ sm(gap 10、上下 12 左右 16);14/20 正文色、radius-md、1px 边框; 标题 14/20 500 强调色、行内 gap 8;说明两行截断;header / footer 占满整行两端对齐。空插槽连同它两侧的 gap 一起收起 (照 pt-card 看宿主子节点 + slotchange + MutationObserver,坑 27)。href时内部渲染<a>(target/rel,_blank自动补noreferrer noopener),带状态层与焦点环、delegatesFocus。圆角经--pt-item-radius覆盖。 - pt-item-group:
flex-direction: column,宿主默认role="list"(已有 role 不覆盖);直接子元素的 pt-item 在接入 DOM 时 自己补role="listitem",挪出组时只撤自己补的。与 pt-kbd-group 一样是容器而不是子元素,不标@childOf,与 pt-item 共用文档页。 - pt-item-media(
@childOf pt-item):variantdefault(svg 20 弱色)/ icon(32 radius-sm 描边 + bg-secondary,svg 16 正文色)/ image(40 radius-sm,::slotted(img)铺满 cover)。
- pt-item:源包 Item / ItemContent / ItemTitle / ItemDescription / ItemActions / ItemHeader / ItemFooter 合成一个元素, slot
b466c25: 新增滚动区域 pt-scroll-area(批 4 · C4.5):
- 滚动走浏览器原生(滚轮、触控板、触屏惯性、键盘、查找定位不打折扣),只把原生滚动条藏掉、换成自绘细滚动条: 竖条宽 10 / 横条高 10、padding 1、起始侧 1px 透明边,thumb 圆角满、
--pt-border-default(经--pt-scroll-area-thumb-color覆盖)。 orientationvertical(默认)/ horizontal / both;typehover(默认)/ scroll / auto / always 与scroll-hide-delay(600) 同 Radix 语义;hover 模式下视口经键盘聚焦时也显示。thumb 长度按可见 / 内容比例(最短 18),拖动用 pointer capture, 点轨道空白处朝那一侧翻一页(87.5%);ResizeObserver 盯视口与内容,容器尺寸变化、内容增减时跟随,不溢出时不显示。 RTL 下竖条换到左侧、横条从右端起步(逻辑属性 + scrollLeft 负数换算)。触屏下 thumb 命中区扩到 44px。- 宿主是纵向 flex 容器,给
height或只给max-height都能滚动。 - 可达性:溢出时视口
tabindex=0,有label时是role="region"地标,否则以新增的 i18n 文案scrollArea.label(可滚动区域 / Scrollable region)命名的role="group";自绘滚动条aria-hidden。 - 方法
scrollToPosition(options)转发到视口的scrollTo()(scrollTo是元素原生方法,Stencil 保留名,不能同名)。 - part
viewport/scrollbar/thumb。
- 滚动走浏览器原生(滚轮、触控板、触屏惯性、键盘、查找定位不打折扣),只把原生滚动条藏掉、换成自绘细滚动条: 竖条宽 10 / 横条高 10、padding 1、起始侧 1px 透明边,thumb 圆角满、
4cacf8b: 新增标记 pt-marker(批 4 · C4.6):对话流 / 列表 / 时间线里标出一个节点的一行弱色说明(「今天」「以下为新消息」)。
- 源包 Marker / MarkerIcon / MarkerContent 合成一个元素:slot
icon(16px、aria-hidden)+ 默认插槽(文字)。 满宽横向 flex、最小高 16、gap 8、14/20 弱色、起始侧对齐。没放图标时图标位连同 gap 一起收起 (看宿主子节点 + slotchange + MutationObserver,坑 27)。 variantdefault / separator / border:separator 两侧各一条 1px--pt-border-default细线(真实元素part="line", 不用伪元素),线与文字之间的 4px 用逻辑方向(源包是物理 mr / ml),RTL 下照样对称;文字居中、不伸长, 过长时允许收缩折行(源包 flex-none 会连同两侧间距溢出容器)。border 底边 1px、pb 8。href时内部渲染<a>(target/rel,_blank自动补noreferrer noopener):下划线 offset 4、悬停变 standard、 焦点环、delegatesFocus,触屏命中区经.touch-target外扩到 44px。默认插槽直接子元素的<a>经::slotted(a)得到同样的下划线与悬停色、颜色随正文(嵌套更深的由使用方自己写)。- 开发站与文档站新开「对话 / AI」分组(源包归在 Chat / AI,批 5 的同类组件接着放)。
- 源包 Marker / MarkerIcon / MarkerContent 合成一个元素:slot
1f2e8bb: 新增
pt-input-otp验证码输入:定长格子、groups分组、单字符pattern(默认只收数字,移动端弹数字键盘),ptChange+ 填满时的ptComplete,参与表单(空值必填、不满位数报 tooShort),Enter 提交所属表单。 格子是视觉层,接收输入的是一个透明原生 input:短信验证码自动填充(one-time-code)、粘贴、输入法都走原生路径。 已登记textControls(Vuev-model/ AngularngModel)与 pt-field 的控件清单。locale 注册表删去未使用的
otp.digit,新增otp.incomplete(「请输入完整的 {length} 位验证码」)。 自定义 locale 里写了otp.digit的要改成新 key。39dca9b: 首个公开版本。
设计变量(
@ptengine/tokens):原语与语义两层,成对导出包装形态与通道形态; 明暗主题通过在根节点切换一组变量实现,支持任意嵌套的局部主题孤岛。基础组件(
@ptengine/ui):Button、Input、Textarea、Select / Option、Checkbox、Switch、 Badge、Spinner、Modal、Tooltip、EmptyState。全部 Shadow DOM,表单类组件参与原生表单, 弹层类组件在窄屏下切换为底部抽屉,触控目标不小于 44px。业务组件(
@ptengine/widgets):目前是占位,只有pt-provider依赖注入;业务组件以后再加。框架包装:React、Vue 3、Angular 19+ 三套包装层由构建自动生成,覆盖 SSR。
5e1594e: Vue / Angular 包装层改为真正按需:只用
PtButton时产物从约 120KB 降到约 25KB(gzip),不再打进全部组件。@ptengine/ui/dist/components/<tag>.js改为无副作用(只导出类与defineCustomElement,import 了不注册), 包装层从这里导入并自己注册;@ptengine/ui/components/<tag>.js仍是 import 即注册,原生用法不变。 直接导入dist/components/<tag>.js并依赖它自动注册的代码,要改用components/<tag>.js或手动调defineCustomElement()。 两个内核包新增sideEffects声明。9126cf4: 正确性修复(优化方案 P0)。
业务组件:
requestClient不再在第二次通知时把自己退订(切换 client 只能切一次的 bug);pt-provider挂载时广播context-provider,断开时不再清空订阅者,同值不重复通知。基础组件:
setLocale()现在会让已渲染组件即时更新文案;checkbox 的英文硬编码改走 i18n; select / textarea / switch 补上表单校验(valueMissing/customError),switch 新增required/invalid; select 的弹层定位改到componentDidRender,click-outside 与定位抽到utils/overlay.ts,抽屉样式抽到utils/drawer.css; select 补aria-controls/aria-activedescendant;tooltip 补aria-describedby、触屏兜底与视口翻转; 组件级 CSS 变量改为使用处 fallback(使用方可覆盖);删掉永不匹配的::slotted规则;清空按钮可 Tab 到达并满足 44px; modal 卸载时不再抢回焦点,关闭按钮exportparts;去掉无用的 puppeteer 依赖。 API 对齐(0.x):select 的ptClosedetail 统一为{ reason };select / checkbox / switch 补ptFocus/ptBlur; 每个组件都有part="base";checkbox / switch 统一只在空格切换。tokens:新增语义 token
--pt-bg-overlay(暗色下另有取值),z-overlay提到 60;.pt-theme不再强制亮色;[lang]的字体规则从 tokens.css 挪到 ui 的全局样式;verify.mjs支持比对值而不只比名字。
Patch Changes
d523000:
utils/motion.css按用途拆成utils/motion/下的小文件(fade/zoom/slide-in/slide-out/spin/pulse/caret), 减少动态效果时的退化规则跟着对应文件走;motion.css只保留为汇总与说明。Stencil 会把
@import的 CSS 整份内联进每个引用它的组件:pt-modal / pt-select / pt-tooltip / pt-spinner 改为只引用自己用到的文件,各自不再携带用不到的 keyframes。编译产物里的animation声明与被引用的 keyframes(含prefers-reduced-motion下的重定义)与之前逐条相同,观感不变。8468bfd: 新增弹层共用的定位与顶层机制
utils/floating(批 0 · B1),pt-tooltip先切过去作回归验证。computePosition(anchor | {x, y}, floating, { placement, align, offset, padding, dir })返回视口坐标与翻转后的实际方位: 放不下翻到对侧,交叉轴 clamp 进视口,RTL 下 start / end 对调;纯函数版computePositionFromRects便于单测。openFloating(anchor, floating, opts) → cleanup:弹层加popover="manual"并showPopover()进顶层, 不再被祖先的overflow: hidden裁切、不受transform祖先与层叠上下文影响;DOM 位置不动。 打开期间跟随滚动、窗口与尺寸变化(rAF 合并)。不支持 popover API 时退回同一套坐标的position: fixed。onOutsidePointerDown新增;onOutsideClick迁入同一文件(utils/overlay仍转出,pt-select不受影响)。pt-tooltip:公开 API 与交互不变;气泡改为顶层 + 视口坐标定位,放在overflow: hidden容器里也能完整显示, 靠近视口左右边缘时不再被切掉一半(交叉轴 clamp)。
6be7ef7: 批 0 · B3:新增
utils/listbox,pt-select切到utils/floating顶层定位。公开 API 不变。utils/listbox.ts:moveActive(current, move, { count, isDisabled, loop, pageSize })在选项间移动高亮项 (上下 / Home / End / PageUp / PageDown,跳过禁用项,是否循环可配),listboxMoveForKey把按键翻译成移动方式;createTypeahead({ timeout, now })/findTypeaheadMatch按字母跳项(前缀累积、超时清空、从当前项之后找、 同一字母连按循环)。纯函数 / 小状态对象,后续 combobox / menu / command 共用。utils/floating.ts新增showInTopLayer(element):只进顶层、不定位,给遮罩、贴底抽屉这类位置由 CSS 决定的层用;openFloating内部也改走它。pt-select:列表经openFloating进顶层(bottom、start 对齐、间距 4px、至少与触发器同宽),放在overflow: hidden/transform容器里不再被裁切;下方放不下翻到上方、交叉轴 clamp 进视口。 窄屏抽屉与遮罩也进顶层,始终贴视口底边、遮罩盖住整页,不受祖先transform影响。 字母跳项改为从当前项之后找、同一字母连按在以它开头的选项间循环(与原生 select 一致);其余键盘行为不变。- 删除
utils/overlay.ts(computePlacement已无引用,onOutsideClick一直在utils/floating.ts)。
3e676b6: 批 0 · B4 + B7:抽出共享动效与列表项样式,观感不变。
- 新增
utils/motion.css:pt-fade-in/out、pt-zoom-in-95/out-95、pt-slide-in-from-*/pt-slide-out-to-*(距离取--pt-motion-distance,默认--pt-space-1)、pt-spin、pt-pulse、pt-caret-blink。 减少动态效果时位移、缩放类退化为纯淡入 / 淡出。只由用到的组件@import,不进全局样式表。 - pt-modal / pt-select / pt-tooltip / pt-spinner 的私有 keyframes 收编进来:
pt-zoom-in→pt-fade-in, pt-zoom-in-95,pt-select-in→pt-fade-in, pt-slide-in-from-top,pt-tooltip-in→pt-fade-in;pt-spin/pt-pulse同名迁入。 时长、缓动、终态与之前逐项相同。 - 新增
utils/list-item.css(选项 / 菜单项 / 命令项共用的一行),pt-option 改为引用它。
- 新增
6da0023: 批 1 · C1.1:控件宿主 CSS 约定。
- pt-button / pt-input / pt-select 的控件高度从内部元素挪到
:host(:host([size])换档),带边框的内部元素width: 100%; height: 100%撑满宿主;pt-button 纯图标(icon-only)的宽度同样挪到宿主,variant="link"宿主不定高。 使用方写在元素上的class="h-7 w-7"/style="width: 100%"现在能改变控件的实际尺寸。默认尺寸与观感不变。 docs/component-api.md §7写入该约定(pt-checkbox / pt-switch 例外及原因),并补充光 DOM 元素的适用范围。- 生成器:新增
--light生成shadow: false的光 DOM 元素(样式段追加到全局样式表);模板不再在:host上声明组件级变量。
- pt-button / pt-input / pt-select 的控件高度从内部元素挪到
d23da85: 修复弹层互相嵌套时外层被连带关闭的两个问题(批 2 收口的弹层专项 e2e 抓出来的):
- 弹层的开关事件改为不冒泡:
pt-modal/pt-popover/pt-hover-card/pt-tooltip/pt-menu/pt-context-menu/pt-select的ptOpen、ptRequestClose、ptClose。以前弹窗里的菜单按 Esc 关闭时,它的ptClose冒泡到弹窗宿主, React / Vue / Angular 里弹窗上的onPtClose/@pt-close/(ptClose)(受控写法通常是setOpen(false))把弹窗也关了; 弹窗上ptRequestClose里的preventDefault()同样会误拦内层弹层的关闭。要在外层容器上统一监听,改用捕获阶段。 pt-select点触发器展开时把焦点放到触发器上。Safari 点按钮不给焦点(原来聚焦着的还会落回 body), 列表的方向键 / Esc 都收不到;放在气泡卡片里时 Esc 越过下拉直接关掉了气泡卡片。
- 弹层的开关事件改为不冒泡:
ec35c3e: 修复 pt-input / pt-textarea 的值在两次改动落在同一次重渲染之前时,内部原生控件不跟着更新的问题:
- 搜索框(
type="search")刚输入、还没来得及重渲染就按 Esc,宿主value已清空,框里的字却还在。 - 受控用法里在
ptChange回调中把value写回原值(拒绝这次输入),原生控件仍显示用户刚敲的内容。
现在
value一变就同步写进内部 input / textarea(只在不一致时写,正常输入不影响光标)。- 搜索框(
a1efb75: 弹层打开期间跟随锚点位移(批 2 附加修复):
openFloating打开期间每帧比较一次锚点的getBoundingClientRect(),变了才重算坐标。此前只在 scroll、window resize 与 ResizeObserver 时重算, 锚点尺寸不变但被推走(上方的pt-accordion/pt-collapsible展开、图片加载、框架晚一拍渲染)时 弹层停在原处。pt-select、pt-tooltip、pt-popover同时受益;原先的 scroll 监听与锚点 ResizeObserver 被逐帧比较覆盖,一并去掉。API 不变。aeb7c1c: pt-popover 被嵌进别的组件影子树时,关闭仍能把焦点还给触发元素:判断「焦点在不在浮层内容里」改为按宿主所在的树取
activeElement(新内部工具utils/dom的activeElementAround),不再读只会给到最外层宿主的document.activeElement。910922b: pt-popover 的
modal补全为完整的模态(按 Radix 的模态 Popover):以前只把焦点关在浮层里,现在另外- 浮层下面铺一层透明遮罩(进顶层),点外面只关浮层(
reason: 'outside'),不会点到页面上的元素; - 页面其余部分设为
inert(读屏看不到、Tab 不进去;aria-live播报区不动,使用方自己设的inert保留); - 锁住页面滚动;
- 关闭后焦点还给触发元素,包括点外面关闭的情况。
打开期间改
modal立即生效。浮层打开期间再弹出的 pt-modal / pt-sheet 若挂在被隐藏的区域里,会在打开期间把自己放出来。- 浮层下面铺一层透明遮罩(进顶层),点外面只关浮层(
e10e203: 批 3 收口修复:
- pt-checkbox / pt-switch:没有标签文案时不再在方框(轨道)后面多留 8px。原来靠
.label:empty隐藏,但标签容器里始终有<slot>, 这条规则从没生效过。现在按默认插槽里有没有文案判断(纯空白与带具名 slot 的元素不算),文案中途加上或去掉会跟着切换。 - pt-input:触屏(
pointer: coarse)下内部原生<input>的命中区不小于 44px,宿主和描边框的视觉尺寸不变(同 pt-native-select 的做法)。 pt-textarea 最小 3 行,本来就高于 44px,不需要改。 - pt-popover:打开着被移出 DOM 又插回来时(框架列表重排、换父节点),重新进顶层、定位,恢复 Esc / 点外面关闭,
modal时恢复 页面 inert、焦点陷阱与滚动锁;不重复发ptOpen。pt-modal / pt-sheet / pt-drawer 已有这项处理。 - pt-sheet:关闭时只等会结束的退场动画。使用方经
::part()挂的无限循环动画不会再让面板一直停在 closing、占着顶层(同 pt-drawer)。 - pt-combobox / pt-select / pt-radio-group / pt-textarea / pt-checkbox / pt-switch:每轮渲染后对齐一次表单值与校验状态。 按需注册时组件刚升级那一刻被写进来的
required/invalid(如 pt-field 下发、Angular 在<form>里写值)不触发@Watch, 以前会出现「界面显示必填、表单却不拦提交」(Angular 下 pt-field 包 pt-combobox 实测复现)。
- pt-checkbox / pt-switch:没有标签文案时不再在方框(轨道)后面多留 8px。原来靠
d93a720: 批 3 遗留债:
- pt-hover-card / pt-menu / pt-context-menu / pt-tooltip / pt-select:打开着被移出 DOM 又插回来时(框架列表重排、换父节点), 重新进顶层、定位,恢复 Esc / 点外面关闭,不重复发
ptOpen(同 pt-popover)。菜单与右键菜单把焦点放回面板(打开前的焦点仍在关闭时归还), 下拉选择器把焦点放回触发器,方向键照常可用;提示重新挂上aria-describedby。 - pt-modal:没有
description/footer插槽内容时,两块连同间距一起收起,没有说明时面板也不再挂指向空内容的aria-describedby。原来靠:empty隐藏,但容器里始终有<slot>,这条规则从没生效过 —— 只有标题与正文的弹窗 会在标题下、正文下各多出一段间距。现在按宿主子元素判断,打开期间或关着时增删插槽内容都会跟着切换。 - pt-select / pt-combobox / pt-menu / pt-context-menu / pt-menu-sub:触屏(粗指针)下相邻选项、菜单项外扩的 44px 命中区 上下重叠,点在上一项靠下的位置会选中下一项。现在按指针坐标改判为真正要点的那一项(行框包含该点的项,都不包含时取中心 最近的),判定抽成 utils/dom 的
elementAtPoint,与 pt-command 共用。共享 chunk 预算因此从 35KB 上调到 36KB(实测 35.29)。
- pt-hover-card / pt-menu / pt-context-menu / pt-tooltip / pt-select:打开着被移出 DOM 又插回来时(框架列表重排、换父节点), 重新进顶层、定位,恢复 Esc / 点外面关闭,不重复发
c8a0ed7: pt-field / pt-field-set 改走独立的继承通道下发状态,不再改写控件自己的属性(AGENTS.md 坑 35)。
- 以前表单项 / 字段组直接写控件的
required/invalid/disabledattribute:下发期间应用把同名状态设为 true, 撤回时会被一并清掉;受控绑定写回 false 又把下发的撤掉。现在 kit 控件新增内部继承 propinherited-disabled/inherited-required/inherited-invalid(由父元素维护,不建议直接写),有效状态是 自身属性与继承的并集;读el.disabled等得到的始终是应用自己写的值。 - 涉及:pt-input、pt-textarea、pt-select、pt-native-select、pt-combobox、pt-checkbox、pt-switch、pt-radio-group、 pt-slider(三个继承 prop,slider 只有 disabled);pt-button、pt-toggle、pt-toggle-group、pt-radio、pt-field、 pt-field-set(
inherited-disabled,嵌套字段组的内层aria-disabled也跟上)。 - 原生
<fieldset disabled>的formDisabledCallback不再改写disabled,改进内部状态:旧写法在 fieldset 解禁时 不会再收到回调,控件会一直停在禁用态。 - 宿主上给样式用的有效状态改为
data-disabled/data-invalid(组件渲染),不再依赖[disabled]/[invalid]。 - 继承来的禁用与原生禁用一致:不随表单提交、不参与校验。
- 组内原生 input / select / textarea / button 仍直接写原生
disabled(没有别的通道),限制见 field-set 文档。
- 以前表单项 / 字段组直接写控件的
9cc8fe6: 修复 Next.js dev 模式下
pt-collapsible/pt-menu/pt-popover/pt-tooltip引起的 hydration 告警 ("A tree hydrated but some attributes of the server rendered HTML didn't match")。这几个元素会往使用方写的触发元素上补
aria-expanded/aria-haspopup/aria-controls/aria-describedby(以及给没有 id 的内容元素补 id)。服务端渲染出来的元素在包装模块加载时当场升级、赶在 React hydration 之前就写了, React 比对时发现按钮上多出这几个 attribute。ui-react:这四个组件的包装(含
@ptengine/ui-react/ssr)给宿主带上内部标记data-pt-hydrating, 在宿主的 ref 回调里(hydration 比对做完之后)摘掉并通知元素。ui:宿主带着这个标记时,元素先不往光 DOM 里写,放行后按当时的状态补写。没有标记的用法(原生 HTML、Vue、Angular) 行为不变。
生产构建与 dev 下的读屏语义不变:水合完成后 attribute 与以前完全一致,交互后照常更新。公开 API 不变,使用方无需改动。
fa372ce: 包描述文件修正(优化方案 P1)。
- 六个包装包的
exports统一为types/import/default三个条件。 @ptengine/ui与@ptengine/widgets的exports补上 CDN 入口(dist/<ns>/<ns>.esm.js), 文档里让用户加载的路径在打包器里也能解析。
- 六个包装包的
b3a2007: 无障碍修正(优化方案 P2,由 axe 扫描发现):
pt-checkbox的方框、pt-switch的轨道与pt-select的触发器 此前没有可访问名(role 元素本身无内容,<label>包着的又不是原生 input),现在没传aria-label时用aria-labelledby指向插槽里的标签文字 / 当前值。pt-switch的标签文字新增part="label"。a192b6a: 开发站(:3333 / :3334)从平铺单页改成一个组件一页。
/是总览,每个组件一张卡片(带预览片段,改完设计变量一眼看全部);点进去是/button.html这样的单组件页,只加载那一个组件。浏览器的地址栏就是路由,没有写任何 路由代码。侧栏、主题开关(记在 localStorage,翻页不丢)和组件清单来自dev/_shell.js与dev/_nav.js,两个包的外壳文件逐字节相同,只有清单各写各的。顺带修掉一个一直存在、但因为不报错所以没人发现的问题:
packages/*/dev/不在 Stencil 的 watch 范围里(它只递归监听srcDir),此前改 dev 页必须重启 dev server 才生效。 现在登记在watchExternalDirs里。已经开着的 dev server 要重启一次才会生效。pnpm component:new多产出一个 dev 页,并往dev/_nav.js追加一条;新增--cn传中文名pnpm components:check新增五条检查:每个组件在开发站上看得见、有自己那页的必须进侧栏、 每页都引全 global CSS / esm 加载器 /_shell.js、没有指向已删组件的孤儿页。 其中 global CSS 那条此前只是 AGENTS.md 里的口头约定,现在由 CI 守着- 补上此前漏掉的
pt-textarea开发页
5ec427e: 组件 CSS 的行内方向改用逻辑属性(pt-select / pt-modal,以及 pt-option 所用的
utils/list-item.css),为 RTL 预留,LTR 下观感不变;全局样式表新增pt-table光 DOM 样式段(为后续的 pt-table 组件准备)。8e45d0a: tokens:新增三个变量,为 design-components 的 Sheet / AlertDialog / Drawer 等组件迁入做准备。
- 语义色
--pt-bg-overlay-strong:black 80% 的强遮罩,明暗同值。 - 层级
--pt-z-tooltip:默认var(--pt-z-popup),与 popup 同层,只是让气泡可被单独抬高。 - 阴影
--pt-shadow-xl:对齐 Tailwind 3 的shadow-xl。
ui:
pt-tooltip的气泡改用--pt-z-tooltip,默认值不变,视觉无变化。- 语义色
Updated dependencies [3e88d1b]
Updated dependencies [39dca9b]
Updated dependencies [9126cf4]
Updated dependencies [8e45d0a]
- @ptengine/tokens@0.1.0