@q1travel/design-system 0.30.6 → 0.30.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/cascader.md +41 -0
- package/contracts/design.dark.md +6 -3
- package/contracts/design.md +6 -3
- package/contracts/implementation-map.md +8 -0
- package/contracts/interface-guidelines.md +4 -0
- package/contracts/overlay-foundation.md +2 -2
- package/dist-pkg/design-system.d.ts +101 -4
- package/dist-pkg/design-system.js +6529 -5900
- package/dist-pkg/style.css +92 -18
- package/package.json +3 -3
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Cascader
|
|
2
|
+
|
|
3
|
+
Cascader 在已知层级中选择节点。平面搜索使用 Combobox / MultiCombobox;仅显示路径而不逐层浏览的需求不使用 Cascader。
|
|
4
|
+
|
|
5
|
+
## 数据和受控选择
|
|
6
|
+
|
|
7
|
+
`mode` 必填且生命周期内不切换。single 的 value 为 string | null;multiple 为 string[]。onChange 第二参数为完整 CascaderPath 或清除时的 null。value 始终由调用方回写;多选父子独立,新选追加,移除只删该值,无联选、半选或深度限制。
|
|
8
|
+
|
|
9
|
+
CascaderNode 的 value 为全树稳定唯一非空字符串,label 可重复;禁止循环、重复身份及搜索结果与路径末项不一致。children 非空表示已有子级;hasChildren=true 且 children 为空表示待加载。selectable 默认 true,false 仅禁止提交,仍可浏览;disabled 默认 false,禁止选择、浏览与请求,但可到达以读取状态。
|
|
10
|
+
|
|
11
|
+
selectedPaths 优先回显末项,再用已加载节点,再回退 value。远端选择保留组件存活期间的路径快照;持久路径由调用方提供,不自动请求祖先。打开时恢复首个已知选值路径;节点更新时保留有效路径,失效截到最近祖先,不改选择。
|
|
12
|
+
|
|
13
|
+
## 字段与可选省略
|
|
14
|
+
|
|
15
|
+
query 省略为内部空串;传 query 必须配 onQueryChange。关闭保留查询。searchResults 省略本地检索 label 与完整路径;传入包括 [] 即由调用方接管。IME 组合期间仅维护草稿,结束通知最终文字一次。
|
|
16
|
+
|
|
17
|
+
label 便捷入口关联真实 trigger,名称优先 aria-labelledby、aria-label、内置 label。省略 label 由调用方提供外部名称。placeholder 默认「请选择…」,searchPlaceholder 默认「搜索选项…」,emptyText 默认「没有匹配项」,自定义内容替换默认文案且仅一个 role=status。required 默认 false,仅提示,不写无效按钮 aria-required、不自动校验。invalid 默认 false,error 隐含 invalid;errorId、内置错误和 aria-describedby 去重合并。
|
|
18
|
+
|
|
19
|
+
disabled/readOnly 默认 false;只读可聚焦但不能展开、请求、搜索或清除。clearable 默认 false,启用后独立具名按钮清除/移除,不能嵌套触发器。name 每个值输出同名 hidden input,空值无条目,disabled 同步禁用。id/ref 指向真实按钮,className 只到根。onOpenChange 通知每次开闭;无公开 open/defaultOpen。
|
|
20
|
+
|
|
21
|
+
## 异步与焦点
|
|
22
|
+
|
|
23
|
+
onBrowse 接收完整路径,loading/retrying 时不重复;省略不请求。branchStates 的空串键用于根,其余为父节点值;searchState 只管非空查询。状态顺序 loading、error、empty、content。error 必带 message/onRetry,可带 retrying;重试用 RegionErrorState inline/polite 保留按钮,加载用 Spinner。成功时仅当焦点仍在待卸载的区域/重试按钮才转移到列表或搜索;已离开不抢焦点。请求、取消、竞态、缓存、合并与分页归调用方。
|
|
24
|
+
|
|
25
|
+
## Tier A 关联 listbox
|
|
26
|
+
|
|
27
|
+
采用有名称的关联 listbox 列与非空查询的搜索 combobox,不声称 tree 模型。列名为「第 n 级」,n>1 时追加父路径末项 label;当前列一个 Tab 停靠点,aria-activedescendant 指已挂载稳定 option id。option 的选中和活动分离;aria-posinset/setsize 表达完整集合;无交互子项。listbox 不使用浏览器默认 outline,仅 :focus-visible 显示既有 DS 焦点阴影。有子级与仅浏览通过描述表达,不给 option 添加 tree 的 aria-expanded。
|
|
28
|
+
|
|
29
|
+
触发器 Enter/Space/Down 打开聚焦搜索,Up 直接到当前列末项。列 Up/Down、Home/End 在本列移动,端点停止;前缀字符跳转。ArrowRight 进入子级,ArrowLeft 返回父项。Enter/Space 提交可选节点,单选关闭回触发器,多选保持;仅浏览节点进入下一级。非空搜索 Up/Down 在结果移动且输入持焦;Enter 提交;Space/Home/End/左右保留文字编辑。Tab/Shift+Tab 正常经过列头分支选择按钮、当前列、重试及 footer,边界关闭离开,不困焦点。Escape 由组件显式回焦有效触发器并只关闭最上层,不清查询和值。组合输入与 keyCode=229 暂停导航、提交及 Escape。
|
|
30
|
+
|
|
31
|
+
指针(含触屏)点击有子级节点进入下一列;单选可选分支同时提交但保持弹层,叶子提交关闭。多选分支行只浏览,每行行首统一预留 aria-hidden 图形命中区,未选空心框、已选勾选、禁用禁用态、不可选同宽留白;点击图形切换可选节点,叶子行切换。单选不预留。进入可选分支后下一列 listbox 外列头提供 Tab 可达的 DS Button(secondary、sm,前置装饰性空心框/勾选)「选择“<label>”」/「取消选择“<label>”」,click 切换并同步 aria-selected 与按钮名称;窄屏放在返回行下方。键盘行为不变。
|
|
32
|
+
|
|
33
|
+
多选非空时控件只显示标签,不显示可见数量摘要;标签与移除按钮保持 trigger 兄弟,标签换行、控件随内容增高,trigger 覆盖整个控件,标签文字与空白均可打开;移除/清除按钮层级更高且不打开弹层,全部清除与箭头图形固定右上,标签区域预留两者空间,普通与 coarse 指针下均不重叠。describedby 保留完整数量与路径摘要。弹层名称直接为字段可访问名称,不加前缀,无名兜底「选择」;已选标签列表名为「已选项」。
|
|
34
|
+
|
|
35
|
+
Popover 为非模态 dialog,无锁滚动或焦点陷阱。footer 独立于列表,激活关闭;打开新层不得覆盖新焦点。外点保留目标焦点。窗口按列和搜索结果独立测量长文本、字体和触摸高度,保留离屏活项,不以截断冒充窗口化。
|
|
36
|
+
|
|
37
|
+
## 样式和公开边界
|
|
38
|
+
|
|
39
|
+
公开类名:q1-cascader;__label、__control、__trigger、__placeholder、__tags、__tag、__remove、__popup、__panel、__search、__columns、__column、__list、__option、__copy、__path、__forward、__results、__back、__status、__footer、__error。只使用既有 token,不新增刻度。每列优选 space-10 × 6,高度上限 min(space-10 × 6, 40dvh);视口低于 space-10 × 12 + space-6 时同一 Popover 单列钻取,有完整父路径和返回,隐藏列带 hidden,SSR 默认单列。亮暗继承 anchor 局部主题。
|
|
40
|
+
|
|
41
|
+
新增导出 Cascader、CascaderProps、CascaderNode、CascaderPathItem、CascaderPath、CascaderSearchResult、CascaderAsyncState、CascaderBranchStates 与 /components/cascader 文档入口。没有使用端迁移,既有选择器默认行为不变。接入时适配业务规则为标准节点,输出在 onChange 转换;回滚先撤新增调用和路由再撤导出,发布另行授权。
|
package/contracts/design.dark.md
CHANGED
|
@@ -489,7 +489,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
489
489
|
- FormField positions its visually hidden label and empty live status within its own containing block so an overflowing inline row cannot enlarge the page scroll width.
|
|
490
490
|
- Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
|
|
491
491
|
|
|
492
|
-
**Option fields.** ControlOption, Checkbox, Radio, RadioGroup, Choicebox, Combobox, MultiSelect, MultiCombobox, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
|
|
492
|
+
**Option fields.** ControlOption, Checkbox, Radio, RadioGroup, Choicebox, Combobox, MultiSelect, MultiCombobox, Cascader, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
|
|
493
493
|
|
|
494
494
|
**Checkbox task completion circle.** `Checkbox` adds `shape?: 'square' | 'circle'` (default square). Circle-only `tone?: 'neutral' | 'info' | 'warning' | 'danger'` defaults to neutral; `size?: 'sm' | 'md'` defaults to sm. 圆形只用于任务与清单的完成场景;表单多选一律用方形。 Native checkbox, ref, bare accessible names and Space activation remain intact; Enter does not toggle. Circle rejects indeterminate with a development warning. `readOnly` remains focusable, exposes aria-disabled and prevents toggles, hover previews and press feedback; completed readonly circles keep a desaturated fill and tick.
|
|
495
495
|
|
|
@@ -528,12 +528,11 @@ CheckCircleFill accepts `tone?: 'default' | 'success'` (default `default`); succ
|
|
|
528
528
|
|
|
529
529
|
**Images and proportions.** AspectRatio requires a finite positive width/height ratio (invalid values render as 1 with one development warning) and only reserves layout space. Image requires alt (empty for decoration), defaults to contain and eager, accepts optional src, fit, loading, sm/md/lg square sizes (32/40/48), onError and root className; omitted size follows host width and natural image ratio. Loading keeps img mounted under a decorative Skeleton; missing and failed sources show a single named image fallback, and source changes isolate stale events. Decorative states are hidden from assistive technology; neither Image nor its host becomes inert. Inside AspectRatio, omit size: the Image root and img fill the ratio frame and object-fit controls complete centered containment or cropping. No new tokens or existing-consumer migration; remove new usages before reverting the additive exports.
|
|
530
530
|
|
|
531
|
-
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton.
|
|
532
531
|
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton, StatusPage, SkeletonIllustration, EmptyListIllustration.
|
|
533
532
|
|
|
534
533
|
- Banner announces at page scope. Note is an inline aside. ErrorMessage sits against one field. ErrorCard replaces a region that failed to load. Picking the widest one available overstates the problem.
|
|
535
534
|
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data: default `layout="center"` keeps the full empty view; `layout="inline"` is an icon and regular secondary title row inside a section whose heading and actions belong to the host, without description, actions, headingLevel, or illustration. Center accepts a decorative `illustration?: ReactNode` in place of icon; built-in `EmptyListIllustration` depicts a fully processed list or inbox, without an icon backplate. `layout="strip"` adds a full-width neutral strip for list failures and load-more rows, with retry always inside its right edge. See `implementation-map.md` for the retry and announcement boundary.
|
|
536
|
-
- StatusPage defaults to a whole unavailable page with a caller-owned title, decorative SkeletonIllustration, plain-text description rendered as a paragraph, and actions. Its size="region" variant centers an icon or small illustration, title, description, and up to two actions inside a substantial content region, with a transparent background and content-driven height. ErrorCard covers a blocking compact failure; RegionErrorState is a compact retryable region message with its own status announcement; EmptyState means loading succeeded without content. StatusPage creates no live region; its host owns focus and announcements. StatusPage code is metadata only, never a message or spoken number.
|
|
535
|
+
- StatusPage defaults to a whole unavailable page with a caller-owned title, decorative SkeletonIllustration, plain-text description rendered as a paragraph, and actions. Its size="region" variant centers an icon or small illustration, title, description, and up to two actions inside a substantial content region, with a transparent background and content-driven height. ErrorCard covers a blocking compact failure; RegionErrorState is a compact retryable region message with its own status announcement; EmptyState means loading succeeded without content. StatusPage headingLevel accepts 1–6 (default 1); choose the next heading level under the host section. Existing omitted, 1 and 2 calls need no migration. StatusPage creates no live region; its host owns focus and announcements. StatusPage code is metadata only, never a message or spoken number.
|
|
537
536
|
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data. `layout="strip"` adds a full-width neutral strip for list failures and load-more rows, with retry always inside its right edge. See `implementation-map.md` for the retry and announcement boundary.
|
|
538
537
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
|
539
538
|
- Badge and Pill label a state drawn from a known set. Ordinary metadata is text, not a badge. StatusDot is the smallest form, and needs a text label beside it.
|
|
@@ -672,3 +671,7 @@ In this repository, `npm run check` is the full gate; a partial run is not a pas
|
|
|
672
671
|
### 单行控件尺寸
|
|
673
672
|
|
|
674
673
|
显式 sm / md / lg 的单行控件与 Button 对齐为 32 / 40 / 48px;使用现有 `--q1-space-8`、`--q1-control-height-default` 与 `--q1-space-2`。SearchInput 与 Menu 文字触发器默认 md,FilterChip 默认 sm32,Switch 外框 sm / md / lg 同高 32 / 40 / 48px,默认 md。Calendar 的 size 只控制网格,触发器使用 fieldSize。空或缺省 label 不创建可见标签行,消费方须提供可访问名称。粗指针最小 44px 命中规则保持现有契约。
|
|
674
|
+
|
|
675
|
+
## Cascader 层级选择
|
|
676
|
+
|
|
677
|
+
Cascader 用于已知层级中的节点选择,采用关联 listbox + 搜索 combobox,值完全受控、父子独立多选,默认 selectable=true、clearable=false、关闭保留查询。节点可选规则、异步请求与输出转换归调用方。字段、键盘、默认省略、公开类名和加法迁移见 [Cascader 契约](./cascader.md)。无新增 token,既有选择组件默认行为不变。
|
package/contracts/design.md
CHANGED
|
@@ -489,7 +489,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
489
489
|
- FormField positions its visually hidden label and empty live status within its own containing block so an overflowing inline row cannot enlarge the page scroll width.
|
|
490
490
|
- Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
|
|
491
491
|
|
|
492
|
-
**Option fields.** ControlOption, Checkbox, Radio, RadioGroup, Choicebox, Combobox, MultiSelect, MultiCombobox, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
|
|
492
|
+
**Option fields.** ControlOption, Checkbox, Radio, RadioGroup, Choicebox, Combobox, MultiSelect, MultiCombobox, Cascader, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
|
|
493
493
|
|
|
494
494
|
**Checkbox task completion circle.** `Checkbox` adds `shape?: 'square' | 'circle'` (default square). Circle-only `tone?: 'neutral' | 'info' | 'warning' | 'danger'` defaults to neutral; `size?: 'sm' | 'md'` defaults to sm. 圆形只用于任务与清单的完成场景;表单多选一律用方形。 Native checkbox, ref, bare accessible names and Space activation remain intact; Enter does not toggle. Circle rejects indeterminate with a development warning. `readOnly` remains focusable, exposes aria-disabled and prevents toggles, hover previews and press feedback; completed readonly circles keep a desaturated fill and tick.
|
|
495
495
|
|
|
@@ -528,12 +528,11 @@ CheckCircleFill accepts `tone?: 'default' | 'success'` (default `default`); succ
|
|
|
528
528
|
|
|
529
529
|
**Images and proportions.** AspectRatio requires a finite positive width/height ratio (invalid values render as 1 with one development warning) and only reserves layout space. Image requires alt (empty for decoration), defaults to contain and eager, accepts optional src, fit, loading, sm/md/lg square sizes (32/40/48), onError and root className; omitted size follows host width and natural image ratio. Loading keeps img mounted under a decorative Skeleton; missing and failed sources show a single named image fallback, and source changes isolate stale events. Decorative states are hidden from assistive technology; neither Image nor its host becomes inert. Inside AspectRatio, omit size: the Image root and img fill the ratio frame and object-fit controls complete centered containment or cropping. No new tokens or existing-consumer migration; remove new usages before reverting the additive exports.
|
|
530
530
|
|
|
531
|
-
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton.
|
|
532
531
|
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton, StatusPage, SkeletonIllustration, EmptyListIllustration.
|
|
533
532
|
|
|
534
533
|
- Banner announces at page scope. Note is an inline aside. ErrorMessage sits against one field. ErrorCard replaces a region that failed to load. Picking the widest one available overstates the problem.
|
|
535
534
|
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data: default `layout="center"` keeps the full empty view; `layout="inline"` is an icon and regular secondary title row inside a section whose heading and actions belong to the host, without description, actions, headingLevel, or illustration. Center accepts a decorative `illustration?: ReactNode` in place of icon; built-in `EmptyListIllustration` depicts a fully processed list or inbox, without an icon backplate. `layout="strip"` adds a full-width neutral strip for list failures and load-more rows, with retry always inside its right edge. See `implementation-map.md` for the retry and announcement boundary.
|
|
536
|
-
- StatusPage defaults to a whole unavailable page with a caller-owned title, decorative SkeletonIllustration, plain-text description rendered as a paragraph, and actions. Its size="region" variant centers an icon or small illustration, title, description, and up to two actions inside a substantial content region, with a transparent background and content-driven height. ErrorCard covers a blocking compact failure; RegionErrorState is a compact retryable region message with its own status announcement; EmptyState means loading succeeded without content. StatusPage creates no live region; its host owns focus and announcements. StatusPage code is metadata only, never a message or spoken number.
|
|
535
|
+
- StatusPage defaults to a whole unavailable page with a caller-owned title, decorative SkeletonIllustration, plain-text description rendered as a paragraph, and actions. Its size="region" variant centers an icon or small illustration, title, description, and up to two actions inside a substantial content region, with a transparent background and content-driven height. ErrorCard covers a blocking compact failure; RegionErrorState is a compact retryable region message with its own status announcement; EmptyState means loading succeeded without content. StatusPage headingLevel accepts 1–6 (default 1); choose the next heading level under the host section. Existing omitted, 1 and 2 calls need no migration. StatusPage creates no live region; its host owns focus and announcements. StatusPage code is metadata only, never a message or spoken number.
|
|
537
536
|
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data. `layout="strip"` adds a full-width neutral strip for list failures and load-more rows, with retry always inside its right edge. See `implementation-map.md` for the retry and announcement boundary.
|
|
538
537
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
|
539
538
|
- Badge and Pill label a state drawn from a known set. Ordinary metadata is text, not a badge. StatusDot is the smallest form, and needs a text label beside it.
|
|
@@ -672,3 +671,7 @@ In this repository, `npm run check` is the full gate; a partial run is not a pas
|
|
|
672
671
|
### 单行控件尺寸
|
|
673
672
|
|
|
674
673
|
显式 sm / md / lg 的单行控件与 Button 对齐为 32 / 40 / 48px;使用现有 `--q1-space-8`、`--q1-control-height-default` 与 `--q1-space-2`。SearchInput 与 Menu 文字触发器默认 md,FilterChip 默认 sm32,Switch 外框 sm / md / lg 同高 32 / 40 / 48px,默认 md。Calendar 的 size 只控制网格,触发器使用 fieldSize。空或缺省 label 不创建可见标签行,消费方须提供可访问名称。粗指针最小 44px 命中规则保持现有契约。
|
|
674
|
+
|
|
675
|
+
## Cascader 层级选择
|
|
676
|
+
|
|
677
|
+
Cascader 用于已知层级中的节点选择,采用关联 listbox + 搜索 combobox,值完全受控、父子独立多选,默认 selectable=true、clearable=false、关闭保留查询。节点可选规则、异步请求与输出转换归调用方。字段、键盘、默认省略、公开类名和加法迁移见 [Cascader 契约](./cascader.md)。无新增 token,既有选择组件默认行为不变。
|
|
@@ -37,6 +37,10 @@ README owns the repository inventory and generated-file boundary. This file owns
|
|
|
37
37
|
- The public documentation navigation groups are `基础`, `品牌`, and `组件`. Product compositions are owned by downstream consumers.
|
|
38
38
|
- If a capability is missing from a shared component, add the capability to that component before adding another page-local pattern.
|
|
39
39
|
|
|
40
|
+
## Breadcrumb Route Links — Accepted 2026-10-25 (#563)
|
|
41
|
+
|
|
42
|
+
Breadcrumb and CollapsibleBreadcrumb accept an optional `linkComponent` for single-page routing. It replaces only items with a destination that are neither current nor disabled; static items and the collapse button keep their native behavior. Collapsible endpoints, middle levels and short paths use the same adapter. The adapter must render exactly one native `a` root and forward all received anchor properties, including `ref`, `className` and `children`. `LinkComponentProps` keeps a required string `href`. Omitting the adapter preserves native anchors, markup, classes, attributes, focus order and appearance. Consumers opt in per component; existing calls need no migration. Roll back by removing the optional prop before reverting the addition.
|
|
43
|
+
|
|
40
44
|
## Documentation Section Ownership
|
|
41
45
|
|
|
42
46
|
Two component-page sections are q1travel supersets with no external counterpart, so their scope is defined here rather than inferred page by page.
|
|
@@ -141,3 +145,7 @@ Accepted 2026-10-08: explicitly sized single-line Button, ButtonLink, Input, Sea
|
|
|
141
145
|
Owner approved 2026-10-15 (#455): public `RegionErrorState` replaces only a region whose data request failed, while the host keeps its frame (table header, card title, section heading). Props: `title` (required), `description`, `layout` `center` (default; table bodies and large regions, block padding `--q1-space-16` keeps roughly five table rows of height) or `inline` (cards and page sections; the retry button wraps under the description when the component's own container is narrower than 30rem), `headingLevel` 2–6 (default renders `p`, matching EmptyState), `onRetry`, `retrying`, and `live` `polite` (default, `role=status`) or `assertive` (`role=alert`). Accepted 2026-10-25 (#659): `strip` adds `.region-error-state--strip`, a full-width neutral strip with `--q1-color-bg-hover`, `--q1-radius-2`, block padding `--q1-space-2`, inline padding `--q1-space-4`, and minimum block size twice `--q1-space-6`. It shares inline typography and announcements, but its retry always stays inside the right edge without the narrow-container wrap. There is no `size` prop.
|
|
142
146
|
|
|
143
147
|
The announcement region wraps only the icon, the `q1-sr-only` 「错误:」 prefix, the title and the description, and carries `aria-busy` while `retrying`. The retry Button sits outside that region so its loading status never nests inside another live region. Retry uses Button `loading` (focusable, `aria-disabled`, polite 「正在重试」 status) rather than `busy` (native disabled drops focus); the component stays mounted while retrying, and the visible label swaps 「重试」 to 「正在重试」 (hidden from assistive technology because the loading status already announces it). Icons are rendered internally from the existing icon set (AlertCircle, RotateCcw); no icon is exported. Copy is caller-owned; field errors, 403/404 and backend messages are out of scope. After a successful retry the caller moves focus to the restored content. Existing tokens only; the danger color appears only on the icon.
|
|
148
|
+
|
|
149
|
+
## Cascader 层级选择
|
|
150
|
+
|
|
151
|
+
`Cascader`(`src/components/ui/Cascader.tsx`)负责字段及触发器;`cascader/` 的 Types、model、window、Panel 分别负责受控边界、路径与独立选择、测量窗口、关联 listbox 与搜索。复用 Popover/useOverlay、RegionErrorState 和 Spinner;样式来自既有 surface、text、control、space、radius 与 focus token。窄屏同一弹层单列钻取,亮暗继承 anchor。完整约束、可选省略、公开面与回滚见 [Cascader](./cascader.md)。文档入口 `/components/cascader`,不迁移既有选择器。
|
|
@@ -211,6 +211,10 @@ Theming owns tokens and `color-scheme`; the interaction-contrast rule lives in D
|
|
|
211
211
|
|
|
212
212
|
These copy clauses keep reader address and empty states consistent with a formal business console: address the reader as 「您」, omit trailing punctuation from empty-state titles, and explain when content will appear in the description.
|
|
213
213
|
|
|
214
|
+
### Second-Line Text
|
|
215
|
+
|
|
216
|
+
- [both] SHOULD keep second-line text in options and list rows — option descriptions, row subtitles, and secondary labels — empty unless it adds information nothing else provides. Add it only when it carries information the label cannot: items at the same level share the same or easily confused labels and the primary text cannot explain itself, or choosing the item has a result the user cannot see from the label, such as ordering, scope, reach, or timing. Translations, restatements of the label, and decorative notes are not information. The pull request must give the reason. Treat second-line text as an exception patch for the primary message, not as standard layout. Severe consequences such as high-risk, irreversible, or data-overwriting actions are stated in the DestructiveActionModal confirmation, not in option or row second lines. When an external product is used as a reference, its second-line information is not carried over. Body descriptions of panels such as empty states, error states, and dialogs are the main message and are not second-line text; field helper text follows the existing note and tooltip rules.
|
|
217
|
+
|
|
214
218
|
### Component Naming
|
|
215
219
|
|
|
216
220
|
- [repo] SHOULD use the industry-common component name when one exists. Invent a name only when no established name fits. Name what the component is, not how it is built.
|
|
@@ -101,8 +101,8 @@ type OverlayRecord = {
|
|
|
101
101
|
|
|
102
102
|
#### Modal Escape action mode
|
|
103
103
|
|
|
104
|
-
- `Modal` 未传 `dismissal` 时 MUST 完整保留既有 `onClose` 与 `dismissable`
|
|
105
|
-
- `dismissal={{ type: 'escape-action', busy, runAction }}` MUST 与 `onClose` / `dismissable`
|
|
104
|
+
- `Modal` 未传 `dismissal` 时 MUST 完整保留既有 `onClose` 与 `dismissable` 行为。传统模式可选 `outsideDismiss?: boolean` 默认 true;外点关闭条件 MUST 为 `dismissable && (outsideDismiss ?? true)`,Escape 只受 `dismissable` 控制。`outsideDismiss=false` 保留 Escape 关闭与正常关闭后的触发器回焦,`dismissable=false` 同时禁止 Escape 与外点关闭;省略 `outsideDismiss` 时 DOM、SSR 与交互不变。该属性仅属于 Modal,不改变 Sheet 的 outside pointer=false 策略。
|
|
105
|
+
- `dismissal={{ type: 'escape-action', busy, runAction }}` MUST 与 `onClose` / `dismissable` / `outsideDismiss` 在类型层互斥;Foundation 只在该 Modal 为 top-most 且 `busy=false` 时消费 Escape,并同步调用一次 `runAction`。
|
|
106
106
|
- Foundation MUST 使用即时 command latch 阻止同一 tick 的重复 Escape,并在当前 tick 后独立释放该 latch,不得等待或观察 `busy` cycle;`busy=true` 仍作为独立 gate,不得消费 Escape或重复执行。action mode MUST 忽略 outside pointer、不移动当前焦点,也不把 backdrop 暴露为关闭按钮。
|
|
107
107
|
- 消费方 MUST 让 Escape 与 footer Button 调用同一个同步 command;该 command 自行拥有 Promise、即时 latch、`busy`、错误、polite live-region 与结果焦点。错误 live-region MUST 在 action 开始前以空的 `role=status` / `aria-live=polite` 节点常驻,失败时只更新同一节点文本。Foundation 不 await、不吞掉错误,也不从 `busy` 变化推断 action 的成功或失败。`busy` 期间主操作 MUST 以 `aria-disabled` 与 command guard 阻止重复执行,同时保持可聚焦并留在 Tab 顺序中,不得使用原生 `disabled` 移除焦点。
|
|
108
108
|
- action 失败时消费方保持 Modal 挂载、把 `busy` 还原为 false,并在状态提交后调用该 `escape-action` 的 `controlRef.focusInitialIfTopMost()`;该窄 seam 使用 Foundation registry LIFO 判断 Modal 是否仍为 top-most owner,再恢复既定 initial focus target(否则使用同一 Modal 内的合理 fallback),不得以全局 `[role=dialog]` DOM 顺序近似 stack ownership。若上层 Modal 已接管焦点,底层调用 MUST 为 no-op。action 成功并卸载会话树时 Foundation MUST NOT 强制回焦旧 trigger;新目标页面负责接收焦点。
|
|
@@ -202,10 +202,12 @@ export declare type BookProps = {
|
|
|
202
202
|
width?: number | string;
|
|
203
203
|
} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>;
|
|
204
204
|
|
|
205
|
-
export declare function Breadcrumb({ items, label, type, }: {
|
|
205
|
+
export declare function Breadcrumb({ items, label, type, linkComponent, }: {
|
|
206
206
|
items: BreadcrumbCrumb[];
|
|
207
207
|
label: string;
|
|
208
208
|
type?: 'text' | 'menu';
|
|
209
|
+
/** 用于单页应用内路由跳转;省略时渲染原生 a */
|
|
210
|
+
linkComponent?: LinkComponent;
|
|
209
211
|
}): JSX.Element;
|
|
210
212
|
|
|
211
213
|
export declare type BreadcrumbCrumb = {
|
|
@@ -431,6 +433,88 @@ declare type CardOwnProps = {
|
|
|
431
433
|
|
|
432
434
|
export declare type CardProps = CardAsLink | CardAsContainer;
|
|
433
435
|
|
|
436
|
+
export declare function Cascader(props: CascaderProps): JSX.Element;
|
|
437
|
+
|
|
438
|
+
export declare type CascaderAsyncState = {
|
|
439
|
+
status: 'idle';
|
|
440
|
+
} | {
|
|
441
|
+
status: 'loading';
|
|
442
|
+
} | {
|
|
443
|
+
status: 'error';
|
|
444
|
+
message: string;
|
|
445
|
+
onRetry: () => void;
|
|
446
|
+
retrying?: boolean;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
export declare type CascaderBranchStates = Readonly<Record<string, CascaderAsyncState>>;
|
|
450
|
+
|
|
451
|
+
export declare type CascaderNode = {
|
|
452
|
+
value: string;
|
|
453
|
+
label: string;
|
|
454
|
+
children?: CascaderNode[];
|
|
455
|
+
hasChildren?: boolean;
|
|
456
|
+
disabled?: boolean;
|
|
457
|
+
selectable?: boolean;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
export declare type CascaderPath = readonly [CascaderPathItem, ...CascaderPathItem[]];
|
|
461
|
+
|
|
462
|
+
export declare type CascaderPathItem = Pick<CascaderNode, 'value' | 'label'>;
|
|
463
|
+
|
|
464
|
+
export declare type CascaderProps = CascaderSelectionProps & CascaderQueryProps & {
|
|
465
|
+
nodes: CascaderNode[];
|
|
466
|
+
selectedPaths?: readonly CascaderPath[];
|
|
467
|
+
searchResults?: readonly CascaderSearchResult[];
|
|
468
|
+
onBrowse?: (path: CascaderPath) => void;
|
|
469
|
+
branchStates?: CascaderBranchStates;
|
|
470
|
+
searchState?: CascaderAsyncState;
|
|
471
|
+
onOpenChange?: (open: boolean) => void;
|
|
472
|
+
label?: string;
|
|
473
|
+
placeholder?: string;
|
|
474
|
+
searchPlaceholder?: string;
|
|
475
|
+
emptyText?: ReactNode;
|
|
476
|
+
disabled?: boolean;
|
|
477
|
+
readOnly?: boolean;
|
|
478
|
+
required?: boolean;
|
|
479
|
+
invalid?: boolean;
|
|
480
|
+
errorId?: string;
|
|
481
|
+
error?: string;
|
|
482
|
+
clearable?: boolean;
|
|
483
|
+
footer?: ReactNode;
|
|
484
|
+
id?: string;
|
|
485
|
+
name?: string;
|
|
486
|
+
className?: string;
|
|
487
|
+
ref?: Ref<HTMLButtonElement>;
|
|
488
|
+
'aria-label'?: string;
|
|
489
|
+
'aria-labelledby'?: string;
|
|
490
|
+
'aria-describedby'?: string;
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
declare type CascaderQueryProps = {
|
|
494
|
+
query: string;
|
|
495
|
+
onQueryChange: (query: string) => void;
|
|
496
|
+
} | {
|
|
497
|
+
query?: never;
|
|
498
|
+
onQueryChange?: (query: string) => void;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
export declare type CascaderSearchResult = {
|
|
502
|
+
value: string;
|
|
503
|
+
path: CascaderPath;
|
|
504
|
+
disabled?: boolean;
|
|
505
|
+
selectable?: boolean;
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
declare type CascaderSelectionProps = {
|
|
509
|
+
mode: 'single';
|
|
510
|
+
value: string | null;
|
|
511
|
+
onChange: (value: string | null, path: CascaderPath | null) => void;
|
|
512
|
+
} | {
|
|
513
|
+
mode: 'multiple';
|
|
514
|
+
value: string[];
|
|
515
|
+
onChange: (values: string[], changedPath: CascaderPath | null) => void;
|
|
516
|
+
};
|
|
517
|
+
|
|
434
518
|
export declare function Chart({ data, summary, axisLabels, height, className }: ChartProps): JSX.Element;
|
|
435
519
|
|
|
436
520
|
export declare type ChartPoint = {
|
|
@@ -586,12 +670,14 @@ export declare function CollapseGroup({ multiple, children }: {
|
|
|
586
670
|
children: ReactNode;
|
|
587
671
|
}): JSX.Element;
|
|
588
672
|
|
|
589
|
-
export declare function CollapsibleBreadcrumb({ items, label }: {
|
|
673
|
+
export declare function CollapsibleBreadcrumb({ items, label, linkComponent }: {
|
|
590
674
|
items: BreadcrumbCrumb[];
|
|
591
675
|
label: string;
|
|
676
|
+
/** 用于单页应用内路由跳转;省略时渲染原生 a */
|
|
677
|
+
linkComponent?: LinkComponent;
|
|
592
678
|
}): JSX.Element;
|
|
593
679
|
|
|
594
|
-
export declare function Combobox({ options, searchPlacement, searchPlaceholder, footer, value, onChange, onSearch, isLoading, placeholder, size, disabled, readOnly, invalid, errorId, error, emptyText, clearable, id, className, name, inputMode, spellCheck, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ref, ...rest }: ComboboxProps): JSX.Element;
|
|
680
|
+
export declare function Combobox({ options, searchPlacement, searchPlaceholder, footer, value, onChange, onSearch, isLoading, placeholder, size, required, disabled, readOnly, invalid, errorId, error, emptyText, clearable, id, className, name, inputMode, spellCheck, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ref, ...rest }: ComboboxProps): JSX.Element;
|
|
595
681
|
|
|
596
682
|
export declare type ComboboxOption = {
|
|
597
683
|
/** 禁止空字符串(与「未选」的受控空值歧义,adapter 层保证)。 */
|
|
@@ -629,6 +715,7 @@ export declare type ComboboxProps = {
|
|
|
629
715
|
placeholder?: string;
|
|
630
716
|
/** 家族刻度 sm32/md40/lg48(与 Input/Select/Button 同一体系)。 */
|
|
631
717
|
size?: ComboboxSize;
|
|
718
|
+
required?: boolean;
|
|
632
719
|
disabled?: boolean;
|
|
633
720
|
/** 值只读:不可输入、不展开列表。 */
|
|
634
721
|
readOnly?: boolean;
|
|
@@ -1410,6 +1497,13 @@ export declare function Label({ htmlFor, required, hint, children, }: {
|
|
|
1410
1497
|
|
|
1411
1498
|
export declare function Link({ href, tone, external, className, children, ref, ...rest }: LinkProps): JSX.Element;
|
|
1412
1499
|
|
|
1500
|
+
export declare type LinkComponent = (props: LinkComponentProps) => ReactElement | null;
|
|
1501
|
+
|
|
1502
|
+
/** 注入的链接组件收到的属性:原生 a 属性 + 必填字符串 href;实现必须渲染且只渲染一个原生 a,并透传全部属性(含 ref、className、children)。 */
|
|
1503
|
+
export declare type LinkComponentProps = Omit<ComponentPropsWithRef<'a'>, 'href'> & {
|
|
1504
|
+
href: string;
|
|
1505
|
+
};
|
|
1506
|
+
|
|
1413
1507
|
export declare type LinkProps = {
|
|
1414
1508
|
/** 目标地址。省略时渲染为静态文本,不表示 disabled 链接。 */
|
|
1415
1509
|
href?: string;
|
|
@@ -1601,6 +1695,7 @@ declare type ModalEscapeActionProps = {
|
|
|
1601
1695
|
dismissal: ModalDismissal;
|
|
1602
1696
|
onClose?: never;
|
|
1603
1697
|
dismissable?: never;
|
|
1698
|
+
outsideDismiss?: never;
|
|
1604
1699
|
};
|
|
1605
1700
|
|
|
1606
1701
|
export declare function ModalInset({ children }: {
|
|
@@ -1611,6 +1706,8 @@ declare type ModalLegacyDismissalProps = {
|
|
|
1611
1706
|
dismissal?: never;
|
|
1612
1707
|
onClose: () => void;
|
|
1613
1708
|
dismissable?: boolean;
|
|
1709
|
+
/** 是否允许外点关闭;默认 true,且仅在 dismissable 允许时生效。 */
|
|
1710
|
+
outsideDismiss?: boolean;
|
|
1614
1711
|
};
|
|
1615
1712
|
|
|
1616
1713
|
export declare type ModalProps = ModalBaseProps & (ModalLegacyDismissalProps | ModalEscapeActionProps);
|
|
@@ -2445,7 +2542,7 @@ export declare type StatusPageProps = {
|
|
|
2445
2542
|
secondaryAction?: ReactNode;
|
|
2446
2543
|
illustration?: ReactNode;
|
|
2447
2544
|
icon?: ReactNode;
|
|
2448
|
-
headingLevel?: 1 | 2;
|
|
2545
|
+
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
2449
2546
|
};
|
|
2450
2547
|
|
|
2451
2548
|
export declare function Switch({ options, ariaLabel, name, defaultValue, value, onChange, size, variant, fullWidth, iconOnly, }: {
|