API
nsh/base
nsh/base 是应用界面的基础组件层:不带样式、负责行为、状态、焦点和无障碍的组件,各平台通用。它位于基础元素(nsh 和各个 nsh/<module> 模块)与应用自己的组件库之间,就像 GPUI Kit 中 gpui-base 位于 gpui 与带样式的组件库之间(见模块)。它提供控件(Button、Checkbox、放在 RadioGroup 里的 Radio、Switch、放在 Tabs 里的 Tab)、可展开和收起的区域(Collapsible)、进度条(Progress)、由用户自行安排的工作区(放在 ResizablePanelGroup 里的 ResizablePanel,以及 DockArea 的分栏、标签组和停靠区)、Markdown 或 HTML 文档(TextView)、代码编辑器(Editor)、文本之间的改动(DiffView)、跨文字的选区(SelectionArea)、显示在一切之上的弹出层(Popover,Menu 和 ContextMenu 及其 MenuItem、MenuSeparator 和 Submenu,Tooltip,Sheet)、toast(放在 ToastStack 中的 Toast,生命周期由 ToastManager 管理)、导航(由 createNavStack 创建的状态驱动、由多个 NavPage 组成的 NavStack,以及在所有内容之上展示内容的 Dialog)、安全区(SafeAreaView、safeArea() 和 safeAreaInsets())、系统警告对话框(AlertDialog)、软键盘(Keyboard、keyboardFrame() 和 KeyboardAvoidingView)。页面和对话框还决定状态栏(statusBar)。
外观由应用决定:页面、对话框或标题栏里的内容都是你自己的 JSX,用基础元素配合样式、Tailwind 类和主题绘制;或者是你自己的 crate 中的 Rust 组件;也可以是社区基于 nsh/base 构建的组件库。每个宿主都提供 nsh/base;Rust 应用通过 nsh-base crate 使用同一套 API(nsh_base::{button, safe_area_view, …})。
use nsh_base::{
AvoidBehavior, Edge, MarkdownExtension, MarkdownOptions, alert_dialog, button, checkbox, dialog, diff_view, dock_area, editor,
h_resizable, keyboard_avoiding_view, nav_page, nav_stack, radio, radio_group, resizable_panel, safe_area_view,
selection_area, switch, tab, tabs, text_view, theme, toast, toast_stack, v_resizable,
};import {
AlertDialog, Button, Checkbox, Collapsible, ContextMenu, createNavStack, Dialog, DiffView, DockArea, Editor, Keyboard, keyboardFrame, KeyboardAvoidingView, Menu, Progress,
MenuItem, MenuSeparator, NavPage, NavStack, Popover, Radio, RadioGroup, ResizablePanel, ResizablePanelGroup, SafeAreaView, safeArea, safeAreaInsets, SelectionArea, Sheet,
Submenu, Switch, Tab, Tabs, TextView, Toast, ToastManager, ToastStack, Tooltip,
} from "nsh/base";
import type {
AlertDialogAction, AlertDialogOptions, AlertDialogPresentation, AlertDialogRole, AvoidBehavior, ButtonProps,
CheckboxProps, CollapsibleProps, DialogChangeReason, DialogProps, DiffAnnotation, DiffFile, DiffHunk, DiffLineRange, DiffSeparator, DiffStats, DiffViewProps, DiffViewRef, DockAreaLayout, DockNode, DockRegion, EditorDecoration, EditorDiagnostic, EditorProps, EditorRef, EditorSearch, EditorSearchResult, EditorSelection, Edge, Edges, HeaderStyle, NavPageProps, NavStackProps, NavStackState, ProgressProps, StatusBar,
RadioGroupProps, RadioProps, SelectionAreaProps, StatusBarForeground, SwitchProps, TabProps, TabsProps, TextViewProps, TextViewDocument, MarkdownExtension, MarkdownOptions,
ToastEntry, ToastMotion, ToastOptions, ToastProps, ToastStackProps, ToastTransitionStatus,
} from "nsh/base";本页是模块的索引:每个组件都有自己的元素页面,包含示例、属性和平台说明。完整的上手讲解见导航指南和表单与键盘。
| TSX | Rust | 是什么 |
|---|---|---|
Button | button() | 带禁用和选中状态、面向屏幕阅读器有角色的按压目标。 |
Checkbox | checkbox() | 勾选 / 未勾选 / 半选控件,指示器及其中的标记由应用设置。 |
Radio、RadioGroup | radio()、radio_group() | 一组只能选一个的选项中的一项,以及这一组;勾选哪一个由应用持有。 |
Switch | switch() | 开 / 关控件,轨道和滑块由应用设置样式。 |
Tab、Tabs | tab()、tabs() | 标签列表中的一个标签页,以及这个列表;选中哪一个由应用持有,内容也由应用显示。 |
Collapsible | collapsible() | 内容可展开和收起的区域;open 和触发它的控件由应用持有。 |
Progress | progress() | 进度条;value(0 到 100)和 indeterminate 由应用持有。 |
ResizablePanelGroup、ResizablePanel | h_resizable()、v_resizable()、resizable_panel() | 并排或上下排列的面板,用户拖动它们之间的手柄调整大小。 |
DockArea | dock_area() | 由分栏、标签组和边缘停靠区组成、用户自行安排的工作区;它的布局是应用的数据。 |
NavStack、NavPage、createNavStack | nav_stack()、nav_page() | 原生导航栈及其页面。 |
Dialog | dialog() | 在所有内容之上呈现的内容:sheet、全屏流程、对话框。 |
SafeAreaView | safe_area_view() | 避开系统界面和标题栏的视图。 |
safeArea()、safeAreaInsets() | safe_area()、cx.insets() | 边距本身,响应式。 |
KeyboardAvoidingView | keyboard_avoiding_view() | 让内容保持在软键盘上方的视图。 |
Keyboard.dismiss()、keyboardFrame() | dismiss_keyboard()、keyboard_height() | 隐藏键盘;键盘在界面上的高度。 |
AlertDialog | alert_dialog() | 平台的警告框或操作表。 |
TextView | text_view() | 用文字和视图绘制的 Markdown、HTML 或文档块组成的文档,适用于流式输出的 AI 回复。 |
Editor | editor() | 代码编辑器:语法高亮、多光标、折叠、查找与替换、诊断。 |
DiffView | diff_view() | 两段文本之间或一个补丁中的改动,并排或合并显示,带针对每处改动的操作。 |
SelectionArea | selection_area() | 让其中的文字共用一个选区。 |
Popover | popover() | 锚定在触发元素上、显示在一切之上的内容。 |
Menu、ContextMenu、MenuItem、MenuSeparator、Submenu | menu()、… | 菜单:桌面上是带级联子菜单的弹出层,手机上是 sheet。 |
Tooltip | tooltip() | 显示在触发元素旁的提示。 |
Sheet | sheet() | 位于屏幕一侧边缘、覆盖在背景层之上的面板。 |
Toast、ToastStack、ToastManager | toast()、toast_stack()、ToastManager | 无样式的 toast:语义根、层叠的栈、它们的生命周期。 |
页面和对话框的 statusBar | .status_bar(..) | 页面或对话框显示期间的状态栏。见状态栏。 |
在 Rust 中,theme() 以设计 token 的形式读取应用的主题(theme().primary、theme().radius),与 TSX 的主题类使用同样的 token。
#控件
#Button
一个只负责行为、没有外观的按压目标:处理按压、禁用和选中状态,并向屏幕阅读器报告它的角色。它的外观是应用的 class 和 style 加上状态变体,内容是它的子元素。见 Button。
#Checkbox
一个只负责行为、没有外观的勾选 / 未勾选 / 半选控件:处理按压、键盘激活、禁用状态,并向屏幕阅读器报告复选框角色和它的值(半选时为部分选中);值由应用持有(checked、indeterminate、onChange)。应用为复选框和它的指示器设置样式,指示器上的 checked:、indeterminate: 和 disabled: 类跟随复选框,应用自己的标记放在指示器里(indicator)。见 Checkbox。
#Radio 与 RadioGroup
一组只能恰好选一个的选项中的一项,以及包住它们的分组。单选框只负责行为、没有外观:处理按压、键盘激活、禁用状态,并向屏幕阅读器报告单选框角色和勾选状态。分组是屏幕阅读器播报的单选框组,本身不持有值:应用告诉每个单选框是否勾选(checked),按下未勾选的单选框时它请求被勾选(onChange)。应用的指示器和标签是单选框的子元素,它们的 group-checked: 类跟随单选框。见 Radio。
#Switch
一个只负责行为、没有外观的开 / 关控件:处理按压、键盘激活、禁用状态,并向屏幕阅读器报告开关角色和它的值;值由应用持有(checked、onChange)。应用为开关、它的轨道和滑块设置样式,轨道和滑块上的 checked: 和 disabled: 类跟随开关。见 Switch。
#Tab 与 Tabs
标签列表中的标签页,以及包住它们的列表。标签页只负责行为、没有外观:处理按压、键盘激活、禁用状态,并向屏幕阅读器报告标签页角色和选中状态。列表是屏幕阅读器播报的标签列表,本身不持有值:应用告诉每个标签页是否选中(selected),接收按压(onPress),并自己显示选中部分的内容。应用的标签是标签页的子元素,它们的 group-selected: 类跟随标签页。见 Tabs。
#Collapsible
内容可展开和收起的区域,没有外观:子元素始终显示(摘要、触发控件),content 在 open 时显示在它们之后。open 和切换它的触发控件由应用持有,应用也在触发控件上向屏幕阅读器报告展开状态。内容只创建一次,收起时保留,所以里面的状态不会丢。见 Collapsible。
#Progress
线性进度条,没有外观:轨道里有一个指示条,指示条的宽度是轨道宽度的 value 百分比。value(0 到 100)和 indeterminate
由应用持有;屏幕阅读器得到进度条角色、范围和数值,不确定时没有数值,此时指示条的宽度和动效由应用决定。见
Progress。
#工作区
#ResizablePanelGroup 与 ResizablePanel
并排或上下排列的面板,每两个面板之间有一个手柄,用户拖动它来分配空间。分组负责拖动(以原生方式运行)、每个面板的限制(minSize、maxSize)和调整大小的光标;尺寸由应用持有(size,通过 onResize 报告),手柄的外观和面板的内容也属于应用。见 Resizable。
#DockArea
可停靠的工作区:面板放在标签组里,标签组并排或上下分栏,左、右、下三条边上各有一个停靠区。布局是应用持有的数据(layout,通过 onLayoutChange 报告);区域负责标签页的拖放、调整大小、停靠区、放大和锁定,面板移动时保留它的内容,在区域较窄时用抽屉和底部面板呈现同一份布局。标签页、手柄、放置指示和面板内容都属于应用。见 Dock。
#文档
#TextView
TextView 用 Text 元素和视图显示 Markdown、HTML 或应用自己构建的文档块:标题、列表、代码、表格、图片、公式、脚注以及应用自己的语法。它为流式输出的 AI 聊天回复而设计,自带一套采用主题颜色的阅读排版,应用可按部件重新设定样式,渲染函数则把应用自己的内容放进文档。见 TextView。
#代码
#Editor
Editor 是支持五十多种语言的代码编辑器:语法高亮、行号、多光标、折叠、软换行、查找与替换、诊断和装饰,以及代码编辑器的按键。文本属于应用(value 和 onChange),查找栏也属于应用,由它驱动编辑器的 search。它带有一套由主题令牌构成的外观,应用可按部件重新设定样式。见 Editor。
#DiffView
DiffView 像代码评审工具那样显示两段文本之间或补丁中各个文件的改动:并排或合并显示,高亮行内改动的单词,未改动的行折叠成可以展开的分隔行,使用 Editor 的语法颜色。应用的操作可以保留或撤销每处改动。见 DiffView。
#选择
#SelectionArea
SelectionArea 让其中的文字一起选择,就像网页上的文字一样:一个选区按阅读顺序跨越视图、Text 元素和 TextView。见 SelectionArea。
#导航
由 NavPage 组成的 NavStack 在 iOS 上是 UINavigationController,在 Android 上是带工具栏和预测性返回的页面栈;在桌面平台和 Web 上,每个页面显示应用自己的 header。见 NavStack,其中也介绍了 createNavStack 和离开页面。
#Dialog
Dialog 在所有内容之上(包括栈的标题栏)、在它自己的原生窗口层中呈现内容。它的弹出层和背景层由应用设定样式。见 Dialog。
#安全区
#SafeAreaView
SafeAreaView 按自己渲染位置的边距为内容添加内边距:最近的页面的(标题栏下方)、最近的对话框的,否则为界面的安全区。见 SafeAreaView。
#safeArea 与 safeAreaInsets
safeArea() 是界面的安全区;safeAreaInsets() 是组件渲染位置的边距。两者都是响应式的 Edges({ top, right, bottom, left },单位为点)。见 safeArea 与 safeAreaInsets。
#警告对话框
#AlertDialog
AlertDialog.show(options) 显示平台自己的警告框或操作表,并以用户选择的操作作为结果。见 AlertDialog。
#键盘
Keyboard.dismiss() 隐藏软键盘,keyboardFrame() 告诉你它覆盖了屏幕的多少。见隐藏键盘和键盘高度。
#KeyboardAvoidingView
KeyboardAvoidingView 让它的内容保持在软键盘上方,并随键盘移动。见 KeyboardAvoidingView。
#状态栏
状态栏属于屏幕上正在显示的内容,和 UIKit 一样:在 UIKit 中,每个视图控制器声明自己的 preferredStatusBarStyle,由最上层的那个决定。在 NSH 中,页面和对话框就是这些视图控制器:NavPage 或 Dialog 用 statusBar 声明它需要的状态栏,应用则在 nsh.json 中声明默认值。
use nsh_base::nav_page;
use nsh_core::ui::prelude::*;
fn photo_viewer() -> Screen {
nav_page().header_visible(false).status_bar(
StatusBarSetting::default()
.foreground(StatusBarStyle::Light)
.hidden(true)
.animated(true),
)
}import { NavPage } from "nsh/base";
function PhotoViewer() {
return (
<NavPage headerVisible={false} statusBar={{ foreground: "light", hidden: true, animated: true }}>
…
</NavPage>
);
}statusBar 接受以下字段,每个都可省略(在 Rust 中可以只传一个 StatusBarStyle,或用 StatusBarSetting 的同名构建方法):
| 字段 | 类型 | 说明 |
|---|---|---|
foreground | "auto" | "light" | "dark"(Rust:StatusBarStyle::Default、Light、Dark) | 内容(时间、电量、信号)的颜色:"light" 用于深色背景,"dark" 用于浅色背景,"auto" 为平台针对当前外观的颜色。 |
hidden | boolean | 隐藏状态栏。 |
animated | boolean | 页面或对话框出现和离开时,以动画呈现变化。 |
这个值是响应式的:statusBar={{ foreground: dark() ? "light" : "dark" }} 会跟随信号变化,读取信号的 Rust 闭包也一样。
#哪个生效
最近显示的页面或对话框决定状态栏,并且逐个字段生效:推入栈的页面位于它下面的页面之上,打开的对话框位于页面之上。它省略的字段取自下面那个,最终取自应用的默认值。页面被弹出或对话框关闭时,下面那个重新生效。
nav_page()
.title("Inbox")
.status_bar(StatusBarStyle::Dark);
// Pushed over it: hidden, with the inbox's dark content when it shows again.
nav_page()
.title("Photo")
.status_bar(StatusBarSetting::default().hidden(true));<NavPage title="Inbox" statusBar={{ foreground: "dark" }}>…</NavPage>
// 推入在它之上:隐藏状态栏;再次显示时沿用收件箱的深色内容。
<NavPage title="Photo" statusBar={{ hidden: true }}>…</NavPage>#应用的默认值
nsh.json 的 "statusBar" 是所有页面和对话框之下的设置,就像 UIKit 应用中 Info.plist 的 UIStatusBarStyle 和 UIStatusBarHidden:
{
"statusBar": { "foreground": "light", "hidden": false }
}
两个字段都可省略。不设置时,内容为 "auto",状态栏显示。打包的 iOS 应用在启动屏上也会使用它。
#各平台上的状态栏
- iOS:隐藏和显示以淡入淡出呈现。在嵌入原生应用的场景下,状态栏的外观由宿主应用的视图控制器决定。宿主可以在其视图控制器中返回
NshAppView.preferredStatusBarStyle、NshAppView.prefersStatusBarHidden和NshAppView.preferredStatusBarUpdateAnimation,以应用 NSH 的设置。 - Android:应用以 edge-to-edge 方式绘制到状态栏之下(Android 15,即 API 35):应用自己的背景就是状态栏的背景,
foreground让状态栏的内容在其上保持清晰。 - 桌面平台与 Web:窗口标题栏和浏览器的栏属于系统和浏览器;
statusBar和nsh.json的"statusBar"用于手机。
{
"capabilities": {
"statusBar": true
}
}
#相关内容
- 导航指南。
- 表单与键盘。
- Button、Checkbox、Radio、Switch、Tabs、Collapsible、Progress、NavStack、Dialog、SafeAreaView、KeyboardAvoidingView、AlertDialog、TextView、Editor、DiffView、SelectionArea、Text 与 Input。
- 设备与环境。
- nsh/android:
BackHandler、用于简短消息的Toast.show及其他 Android 专用 API。