@q1travel/design-system 0.12.0 → 0.14.0
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/README.md +2 -1
- package/bin/q1-design.mjs +1 -1
- package/contracts/design.dark.md +3 -1
- package/contracts/design.md +3 -1
- package/contracts/overlay-foundation.md +17 -0
- package/dist-pkg/design-system.d.ts +52 -1
- package/dist-pkg/design-system.js +3530 -3235
- package/dist-pkg/style.css +193 -16
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -12,10 +12,11 @@ q1travel 产品家族的设计权威上游仓,三件事集于一处:`@q1trav
|
|
|
12
12
|
- Generated mirrors(生成镜像,勿手改):`public/design.md`、`public/design.dark.md` 由 `scripts/sync-design-contracts.mjs` 从契约源逐字节复制;`public/design.css` 由 `scripts/build-design-css.mjs` 把两份契约 frontmatter 的取值注入 `contracts/design.css` 的类词汇后生成,因而自足、可被独立 HTML 页面直接加载。
|
|
13
13
|
- Runtime tokens:`src/styles/tokens.css`(canonical `--q1-*`,仅 `:root` 与 `:root[data-theme='dark']` 两个扁平 selector)。
|
|
14
14
|
- 发布台账:`contracts/releases/`(仅存于仓库,不进 npm 包)。
|
|
15
|
+
- npm 发布与下游自动更新:[发布操作说明](docs/npm-release.md)。
|
|
15
16
|
|
|
16
17
|
## Getting Started
|
|
17
18
|
|
|
18
|
-
前置要求:Node.js 22+(CI 基线 22)与 npm(依赖锁定在 `package-lock.json`);运行 `check:browser` 需要本机 Chrome(可用 `CHROME_PATH`
|
|
19
|
+
前置要求:Node.js 22+(CI 基线 22)与 npm(依赖锁定在 `package-lock.json`);运行 `check:browser` 需要本机 Chrome(可用 `CHROME_PATH` 指定)。`check:browser` 在本地与 CI 都默认使用 `--headless=new`,不探测 TTY 或 `DISPLAY`;只有显式设置 `Q1_DESIGN_BROWSER_HEADLESS=false` 才切换到 headed 模式。
|
|
19
20
|
|
|
20
21
|
```bash
|
|
21
22
|
npm install
|
package/bin/q1-design.mjs
CHANGED
|
@@ -180,7 +180,7 @@ export function findsRawMotionLiteral(lineContent, commentState) {
|
|
|
180
180
|
// 「用户动作到界面回应完毕」有多久;无限循环按定义不是对输入的响应,它的周期是该组件
|
|
181
181
|
// 自己的节奏(spinner 1.2s、skeleton 1.6s),塞进 120–300ms 只会压坏它,或逼出假 token。
|
|
182
182
|
if (/\binfinite\b/.test(segment)) return false;
|
|
183
|
-
//
|
|
183
|
+
// 值整体来自 var() 时不判——例如 `transition: var(--q1-duration-base)`,静态规则不能展开变量内容。
|
|
184
184
|
const withoutVars = segment.replace(VAR_CALL_PATTERN, '');
|
|
185
185
|
return BARE_DURATION_PATTERN.test(withoutVars) || BARE_EASING_PATTERN.test(withoutVars);
|
|
186
186
|
});
|
package/contracts/design.dark.md
CHANGED
|
@@ -378,7 +378,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
378
378
|
- SplitButton is a primary action with an adjacent menu whose first entry mirrors it. Do not use it to hide unrelated actions behind a chevron.
|
|
379
379
|
- IconButton requires an accessible name. An icon-only control without one is not deliverable.
|
|
380
380
|
|
|
381
|
-
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput, Textarea, Select, Fieldset, ErrorMessage.
|
|
381
|
+
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput, VerificationCodeInput, Textarea, Select, Fieldset, ErrorMessage.
|
|
382
382
|
|
|
383
383
|
- FormField owns the label, help text, and error slot. Do not assemble those three around a bare Input.
|
|
384
384
|
- Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
|
|
@@ -535,3 +535,5 @@ Before delivery, confirm each of these. They are observable; none of them is a m
|
|
|
535
535
|
- Nothing scrolls horizontally at the page level.
|
|
536
536
|
|
|
537
537
|
In this repository, `npm run check` is the full gate; a partial run is not a pass. In consuming repositories, run their design and token gates before reporting completion.
|
|
538
|
+
|
|
539
|
+
公开 Foundation 集成:`getOverlayCoordinator` 协调 package 与宿主 overlay 的栈序、inert 与滚动锁;契约见 [Overlay Foundation](./overlay-foundation.md#11-o3-公开-host-协调扩展2026-09-08)。
|
package/contracts/design.md
CHANGED
|
@@ -378,7 +378,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
378
378
|
- SplitButton is a primary action with an adjacent menu whose first entry mirrors it. Do not use it to hide unrelated actions behind a chevron.
|
|
379
379
|
- IconButton requires an accessible name. An icon-only control without one is not deliverable.
|
|
380
380
|
|
|
381
|
-
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput, Textarea, Select, Fieldset, ErrorMessage.
|
|
381
|
+
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput, VerificationCodeInput, Textarea, Select, Fieldset, ErrorMessage.
|
|
382
382
|
|
|
383
383
|
- FormField owns the label, help text, and error slot. Do not assemble those three around a bare Input.
|
|
384
384
|
- Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
|
|
@@ -535,3 +535,5 @@ Before delivery, confirm each of these. They are observable; none of them is a m
|
|
|
535
535
|
- Nothing scrolls horizontally at the page level.
|
|
536
536
|
|
|
537
537
|
In this repository, `npm run check` is the full gate; a partial run is not a pass. In consuming repositories, run their design and token gates before reporting completion.
|
|
538
|
+
|
|
539
|
+
公开 Foundation 集成:`getOverlayCoordinator` 协调 package 与宿主 overlay 的栈序、inert 与滚动锁;契约见 [Overlay Foundation](./overlay-foundation.md#11-o3-公开-host-协调扩展2026-09-08)。
|
|
@@ -242,3 +242,20 @@ D7 q1travel-web adoption/migration
|
|
|
242
242
|
- [q1travel Token Naming](./token-naming.md)
|
|
243
243
|
- [WAI-ARIA APG: Dialog (Modal) Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/)
|
|
244
244
|
- 历史范围证据:`docs/superpowers/specs/w2-overlays-familyization-scoping.md`(非当前决策源)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
## 11. O3 公开 host 协调扩展(2026-09-08)
|
|
248
|
+
|
|
249
|
+
本节是 O3 要求的独立 public API contract,扩展 v1 的禁止 consumer 注入边界。
|
|
250
|
+
授权范围为 package 实现与 Web 本地构建产物联调;不代表发布或生产采用。
|
|
251
|
+
|
|
252
|
+
- `getOverlayCoordinator(document?)` 从包主入口导出;同一 Document 返回同一协调器,SSR 无 document 时返回 null。
|
|
253
|
+
- `register({ id, root, blocking = true, getContainer })` 注册一个宿主实例,返回幂等释放函数。id 在整个 document 栈中必须唯一;root 必须属于该 document 且是 body 直接子节点,非法输入抛错。
|
|
254
|
+
- `getSnapshot()` 返回注册顺序 ids 与 blockingCount;`isTopMost(id)` 使用同一实时栈;`subscribe(listener)` 同步通知登记与释放,返回取消订阅函数。快照供即时读取,非 useSyncExternalStore 的缓存快照。
|
|
255
|
+
- 宿主保留渲染、事件、focus trap 与 focus return;Escape、外点、focus trap 必须先检查共享 top-most。Foundation 不替宿主调用关闭回调,也不绕过 dismissable/busy 策略。
|
|
256
|
+
- 所有 package 与宿主 blocking 实例共享唯一 body overflow/paddingRight 快照和计数。宿主必须移除独立 scroll lock、inert 和快捷键阻断计数;最后一个 blocker 释放才恢复原值,非 LIFO 卸载也适用。
|
|
257
|
+
- 活动 root 被显式登记后不作为背景 inert;原有 inert 状态保留。body 后续新增兄弟节点也纳入背景隔离,最终恢复原值。
|
|
258
|
+
- 多 root 临时使用同一 overlay semantic layer,并按各 root 最新登记实例排列 stacking context;宿主负责自身 root 内部顺序。最后一项释放时恢复 root 原有 position/isolation/zIndex,不删除宿主 root。该扩展不允许宿主覆盖 package root 的归属。
|
|
259
|
+
- 宿主每次 open 对应一次登记/释放;改变 blocking 策略应重新登记。idempotent disposer 支持 StrictMode 清理。不同 document 完全隔离。
|
|
260
|
+
|
|
261
|
+
回归入口:`node --test scripts/check-overlay-coordination.test.mjs`;真实跨 root 的 Escape、焦点、快捷键与滚动锁由 Web consumer 浏览器测试验证。
|
|
@@ -775,6 +775,14 @@ export declare function ErrorMessage({ label, size, action, children, }: {
|
|
|
775
775
|
|
|
776
776
|
export declare type ErrorMessageSize = 'sm' | 'md' | 'lg';
|
|
777
777
|
|
|
778
|
+
/** A host keeps its own rendering, focus and dismissal policy; Foundation owns shared effects. */
|
|
779
|
+
export declare type ExternalOverlayRegistration = {
|
|
780
|
+
id: string;
|
|
781
|
+
root: HTMLElement;
|
|
782
|
+
blocking?: boolean;
|
|
783
|
+
getContainer: () => HTMLElement | null;
|
|
784
|
+
};
|
|
785
|
+
|
|
778
786
|
export declare function Feedback({ label, prompt, placeholder, inline, topics, }: {
|
|
779
787
|
label?: string;
|
|
780
788
|
prompt?: string;
|
|
@@ -845,6 +853,9 @@ export declare type GaugeSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
|
845
853
|
|
|
846
854
|
export declare type GaugeToneDirection = 'quota' | 'health';
|
|
847
855
|
|
|
856
|
+
/** One coordinator per document, shared with all package overlays. Null during SSR. */
|
|
857
|
+
export declare function getOverlayCoordinator(doc?: Document | null): OverlayCoordinator | null;
|
|
858
|
+
|
|
848
859
|
export declare function IconButton({ 'aria-label': canonicalAriaLabel, ariaLabel: legacyAriaLabel, ...rest }: IconButtonProps): JSX.Element;
|
|
849
860
|
|
|
850
861
|
declare type IconButtonAccessibleName = {
|
|
@@ -1169,6 +1180,18 @@ export declare type NoteSize = 'small' | 'default' | 'large';
|
|
|
1169
1180
|
|
|
1170
1181
|
export declare type NoteTone = 'info' | 'success' | 'warning' | 'error' | 'secondary';
|
|
1171
1182
|
|
|
1183
|
+
export declare type OverlayCoordinator = {
|
|
1184
|
+
register: (entry: ExternalOverlayRegistration) => () => void;
|
|
1185
|
+
isTopMost: (id: string) => boolean;
|
|
1186
|
+
getSnapshot: () => OverlaySnapshot;
|
|
1187
|
+
subscribe: (listener: () => void) => () => void;
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
export declare type OverlaySnapshot = {
|
|
1191
|
+
readonly ids: readonly string[];
|
|
1192
|
+
readonly blockingCount: number;
|
|
1193
|
+
};
|
|
1194
|
+
|
|
1172
1195
|
export declare function Pagination({ page, pageSize: pageSizeProp, total, totalPages: totalPagesProp, hasNextPage, onChange, loading, showPageNumbers, summary, pageSizeSlot, prevLabel, nextLabel, compact, className, }: PaginationProps): JSX.Element;
|
|
1173
1196
|
|
|
1174
1197
|
export declare type PaginationCommonProps = {
|
|
@@ -1396,7 +1419,7 @@ export declare const Sidebar: typeof SidebarRoot & {
|
|
|
1396
1419
|
|
|
1397
1420
|
declare function SidebarContent({ className, ...props }: SidebarSlotProps): JSX.Element;
|
|
1398
1421
|
|
|
1399
|
-
export declare function SidebarDrawer({ 'aria-label': ariaLabel, open, onOpenChange, trigger, header, footer, children, collapseBelow, drawerWidth, variant, }: SidebarDrawerProps): JSX.Element;
|
|
1422
|
+
export declare function SidebarDrawer({ 'aria-label': ariaLabel, open, onOpenChange, trigger, header, footer, children, collapseBelow, drawerWidth, variant, dismissOnBackdrop, }: SidebarDrawerProps): JSX.Element;
|
|
1400
1423
|
|
|
1401
1424
|
export declare type SidebarDrawerProps = {
|
|
1402
1425
|
'aria-label': string;
|
|
@@ -1409,6 +1432,7 @@ export declare type SidebarDrawerProps = {
|
|
|
1409
1432
|
collapseBelow?: number;
|
|
1410
1433
|
drawerWidth?: SidebarDrawerWidth;
|
|
1411
1434
|
variant?: SidebarVariant;
|
|
1435
|
+
dismissOnBackdrop?: boolean;
|
|
1412
1436
|
};
|
|
1413
1437
|
|
|
1414
1438
|
export declare type SidebarDrawerTriggerProps = {
|
|
@@ -1771,6 +1795,33 @@ export declare type UseRadioOptions = {
|
|
|
1771
1795
|
|
|
1772
1796
|
export declare function useToast(): ToastContextValue;
|
|
1773
1797
|
|
|
1798
|
+
export declare function VerificationCodeInput({ id, name, value, onValueChange, disabled, readOnly, required, invalid, errorId, size, ref, label, description, error, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': describedBy, }: VerificationCodeInputProps): JSX.Element;
|
|
1799
|
+
|
|
1800
|
+
export declare type VerificationCodeInputProps = {
|
|
1801
|
+
id?: string;
|
|
1802
|
+
name?: string;
|
|
1803
|
+
/** 受控的 0–6 位 ASCII 数字字符串;保留前导零。 */
|
|
1804
|
+
value: string;
|
|
1805
|
+
onValueChange: (value: string) => void;
|
|
1806
|
+
disabled?: boolean;
|
|
1807
|
+
readOnly?: boolean;
|
|
1808
|
+
required?: boolean;
|
|
1809
|
+
invalid?: boolean;
|
|
1810
|
+
errorId?: string;
|
|
1811
|
+
size?: 'md' | 'lg';
|
|
1812
|
+
ref?: Ref<HTMLInputElement>;
|
|
1813
|
+
'aria-describedby'?: string;
|
|
1814
|
+
} & ((FieldShellProps & {
|
|
1815
|
+
'aria-label'?: never;
|
|
1816
|
+
'aria-labelledby'?: never;
|
|
1817
|
+
}) | (FieldBareProps & ({
|
|
1818
|
+
'aria-label': string;
|
|
1819
|
+
'aria-labelledby'?: never;
|
|
1820
|
+
} | {
|
|
1821
|
+
'aria-labelledby': string;
|
|
1822
|
+
'aria-label'?: never;
|
|
1823
|
+
})));
|
|
1824
|
+
|
|
1774
1825
|
export declare function Video({ src, poster, tracks, label, controls, loop, }: VideoProps): JSX.Element;
|
|
1775
1826
|
|
|
1776
1827
|
export declare type VideoProps = {
|