gea-ui-components
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese@geajs/ui Components
@geajs/ui 组件库
@geajs/ui is a component library for the Gea framework that pairs Tailwind CSS styling with Zag.js state machines for accessible, interactive components. It provides ~35 ready-to-use components: simple styled primitives (Button, Card, Input) and behavior-rich widgets (Select, Dialog, Tabs, Toast).
Read in this skill directory for the full component API with props tables.
reference.md@geajs/ui 是面向 Gea 框架的组件库,结合了 Tailwind CSS 样式与 Zag.js 状态机,可提供无障碍、高交互性的组件。它内置了约35个开箱即用的组件:基础样式原语(Button、Card、Input)以及具备丰富交互能力的组件(Select、Dialog、Tabs、Toast)。
你可以阅读本 skill 目录下的 文件,查看包含 props 表格的完整组件 API 文档。
reference.mdSetup
配置步骤
Install
安装
bash
npm install @geajs/core @geajs/ui
npm install -D vite @geajs/vite-plugin tailwindcss @tailwindcss/vitebash
npm install @geajs/core @geajs/ui
npm install -D vite @geajs/vite-plugin tailwindcss @tailwindcss/viteVite config
Vite 配置
js
import { defineConfig } from 'vite'
import { geaPlugin } from '@geajs/vite-plugin'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [geaPlugin(), tailwindcss()],
})js
import { defineConfig } from 'vite'
import { geaPlugin } from '@geajs/vite-plugin'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [geaPlugin(), tailwindcss()],
})Import styles
引入样式
ts
import '@geajs/ui/style.css'ts
import '@geajs/ui/style.css'Import components
引入组件
tsx
import { Button, Select, Dialog, Toaster, ToastStore } from '@geajs/ui'tsx
import { Button, Select, Dialog, Toaster, ToastStore } from '@geajs/ui'Component Categories
组件分类
Simple styled components (no state machine)
基础样式组件(无状态机)
Button, Card (+ CardHeader/CardTitle/CardDescription/CardContent/CardFooter), Input, Textarea, Label, Badge, Alert (+ AlertTitle/AlertDescription), Separator, Skeleton.
These are thin Gea wrappers with Tailwind classes. They accept for custom styling and for content.
ComponentclasschildrenButton、Card(含 CardHeader/CardTitle/CardDescription/CardContent/CardFooter)、Input、Textarea、Label、Badge、Alert(含 AlertTitle/AlertDescription)、Separator、Skeleton。
这些是基于 Gea 封装的轻量组件,内置 Tailwind 类名,支持传入 自定义样式,传入 定义内容。
ComponentclasschildrenZag-powered components (interactive)
Zag 驱动组件(具备交互能力)
Accordion, Avatar, Checkbox, Clipboard, Collapsible, Combobox, Dialog, FileUpload, HoverCard, Menu, NumberInput, Pagination, PinInput, Popover, Progress, RadioGroup, RatingGroup, Select, Slider, Switch, Tabs, TagsInput, Toast (Toaster + ToastStore), ToggleGroup, Tooltip, TreeView.
These extend — a base class that connects Zag.js state machines to Gea's reactivity system. They manage ARIA attributes, keyboard interactions, and focus automatically.
ZagComponentAccordion、Avatar、Checkbox、Clipboard、Collapsible、Combobox、Dialog、FileUpload、HoverCard、Menu、NumberInput、Pagination、PinInput、Popover、Progress、RadioGroup、RatingGroup、Select、Slider、Switch、Tabs、TagsInput、Toast(Toaster + ToastStore)、ToggleGroup、Tooltip、TreeView。
这些组件继承自 —— 这是一个将 Zag.js 状态机与 Gea 响应式系统关联的基类,可自动管理 ARIA 属性、键盘交互和焦点逻辑。
ZagComponentUsage Patterns
使用示例
Basic usage
基础用法
tsx
import { Component } from '@geajs/core'
import { Button, Badge, Separator } from '@geajs/ui'
export default class App extends Component {
template() {
return (
<div>
<Button click={() => console.log('clicked')}>Save</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline" size="sm">Cancel</Button>
<Badge variant="secondary">Draft</Badge>
<Separator />
</div>
)
}
}tsx
import { Component } from '@geajs/core'
import { Button, Badge, Separator } from '@geajs/ui'
export default class App extends Component {
template() {
return (
<div>
<Button click={() => console.log('clicked')}>Save</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline" size="sm">Cancel</Button>
<Badge variant="secondary">Draft</Badge>
<Separator />
</div>
)
}
}Controlled components
受控组件
Zag-powered components follow a controlled pattern: pass (or /) and listen for changes via (or /). Store the value in a class field so Gea's reactivity keeps the UI in sync.
valuecheckedopenonValueChangeonCheckedChangeonOpenChangetsx
import { Component } from '@geajs/core'
import { Select, Switch, Slider } from '@geajs/ui'
export default class Settings extends Component {
theme = ''
darkMode = false
volume = 50
template() {
return (
<div>
<Select
label="Theme"
items={[
{ value: 'light', label: 'Light' },
{ value: 'dark', label: 'Dark' },
{ value: 'system', label: 'System' },
]}
value={this.theme ? [this.theme] : []}
onValueChange={(d: any) => { this.theme = d.value[0] || '' }}
/>
<p>Selected: {this.theme || '(none)'}</p>
<Switch
label="Dark mode"
checked={this.darkMode}
onCheckedChange={(d: any) => { this.darkMode = d.checked }}
/>
<Slider
label="Volume"
value={[this.volume]}
min={0}
max={100}
onValueChange={(d: any) => { this.volume = d.value[0] }}
/>
</div>
)
}
}Zag 驱动组件遵循受控模式:传入 (或 /),通过 (或 /)监听值变化。将值存储在类字段中,Gea 的响应式能力会自动同步 UI。
valuecheckedopenonValueChangeonCheckedChangeonOpenChangetsx
import { Component } from '@geajs/core'
import { Select, Switch, Slider } from '@geajs/ui'
export default class Settings extends Component {
theme = ''
darkMode = false
volume = 50
template() {
return (
<div>
<Select
label="Theme"
items={[
{ value: 'light', label: 'Light' },
{ value: 'dark', label: 'Dark' },
{ value: 'system', label: 'System' },
]}
value={this.theme ? [this.theme] : []}
onValueChange={(d: any) => { this.theme = d.value[0] || '' }}
/>
<p>Selected: {this.theme || '(none)'}</p>
<Switch
label="Dark mode"
checked={this.darkMode}
onCheckedChange={(d: any) => { this.darkMode = d.checked }}
/>
<Slider
label="Volume"
value={[this.volume]}
min={0}
max={100}
onValueChange={(d: any) => { this.volume = d.value[0] }}
/>
</div>
)
}
}Toast notifications
Toast 通知
Toast uses a static class and a component rendered once at the root.
ToastStore<Toaster />tsx
import { Component } from '@geajs/core'
import { Button, Toaster, ToastStore } from '@geajs/ui'
export default class App extends Component {
save() {
ToastStore.success({ title: 'Saved!', description: 'Changes persisted.' })
}
template() {
return (
<div>
<Button click={this.save}>Save</Button>
<Toaster />
</div>
)
}
}Toast methods: , , , , , .
ToastStore.success(opts).error(opts).info(opts).loading(opts).create(opts).dismiss(id?)Toast 依赖静态 类和根节点处全局渲染一次的 组件。
ToastStore<Toaster />tsx
import { Component } from '@geajs/core'
import { Button, Toaster, ToastStore } from '@geajs/ui'
export default class App extends Component {
save() {
ToastStore.success({ title: 'Saved!', description: 'Changes persisted.' })
}
template() {
return (
<div>
<Button click={this.save}>Save</Button>
<Toaster />
</div>
)
}
}Toast 方法包括:、、、、、。
ToastStore.success(opts).error(opts).info(opts).loading(opts).create(opts).dismiss(id?)Dialog
Dialog 对话框
tsx
<Dialog title="Confirm" description="Are you sure?" triggerLabel="Open">
<p>Dialog body content goes here.</p>
</Dialog>tsx
<Dialog title="Confirm" description="Are you sure?" triggerLabel="Open">
<p>Dialog body content goes here.</p>
</Dialog>Tabs
Tabs 标签页
tsx
<Tabs
defaultValue="account"
items={[
{ value: 'account', label: 'Account', content: <p>Account settings</p> },
{ value: 'security', label: 'Security', content: <p>Security settings</p> },
]}
/>tsx
<Tabs
defaultValue="account"
items={[
{ value: 'account', label: 'Account', content: <p>Account settings</p> },
{ value: 'security', label: 'Security', content: <p>Security settings</p> },
]}
/>Cards with form inputs
带表单输入的 Card 卡片
tsx
<Card>
<CardHeader>
<CardTitle>Profile</CardTitle>
<CardDescription>Update your details</CardDescription>
</CardHeader>
<CardContent>
<Label htmlFor="name">Name</Label>
<Input inputId="name" placeholder="Enter your name" value={this.name} />
</CardContent>
<CardFooter>
<Button click={this.save}>Save</Button>
</CardFooter>
</Card>tsx
<Card>
<CardHeader>
<CardTitle>Profile</CardTitle>
<CardDescription>Update your details</CardDescription>
</CardHeader>
<CardContent>
<Label htmlFor="name">Name</Label>
<Input inputId="name" placeholder="Enter your name" value={this.name} />
</CardContent>
<CardFooter>
<Button click={this.save}>Save</Button>
</CardFooter>
</Card>Theming
主题定制
@geajs/ui uses CSS custom properties for theming. Override them in your stylesheet:
css
:root {
--primary: 222 47% 11%;
--primary-foreground: 210 40% 98%;
--radius: 0.75rem;
}Dark mode activates with the class on :
dark<html>html
<html class="dark">Base color CSS variables: , .
--background--foregroundColor CSS variables related to elements containing text (styled with counterpart): , , , , , , .
-foreground--primary--secondary--muted--accent--destructive--card--popoverColor CSS variables related to elements without text: , (input field borders), (focus ring color), .
--border--input--ring--dialog-backgroundOther CSS variables: (base border radius).
--radius@geajs/ui 使用 CSS 自定义属性实现主题能力,你可以在自己的样式表中覆盖这些属性:
css
:root {
--primary: 222 47% 11%;
--primary-foreground: 210 40% 98%;
--radius: 0.75rem;
}给 标签添加 类即可启用深色模式:
<html>darkhtml
<html class="dark">基础色 CSS 变量:、。
--background--foreground与文本容器相关的 CSS 变量(都有对应的 变量):、、、、、、。
-foreground--primary--secondary--muted--accent--destructive--card--popover与非文本元素相关的 CSS 变量:、(输入框边框)、(焦点环颜色)、。
--border--input--ring--dialog-background其他 CSS 变量:(基础圆角值)。
--radiusStyling components
组件样式自定义
- Pass to any component for additional Tailwind classes.
class - Use and
data-partselectors for fine-grained CSS overrides on Zag-powered components:data-state
css
.select-trigger[data-state="open"] { border-color: hsl(var(--ring)); }
.switch-control[data-state="checked"] { background: hsl(var(--primary)); }- 给任意组件传入 即可添加额外的 Tailwind 类名。
class - 对于 Zag 驱动组件,你可以使用 和
data-part选择器实现更细粒度的样式覆盖:data-state
css
.select-trigger[data-state="open"] { border-color: hsl(var(--ring)); }
.switch-control[data-state="checked"] { background: hsl(var(--primary)); }Extending: Creating Custom Zag Components
扩展:创建自定义 Zag 组件
To wrap a new Zag.js machine, extend and implement five methods:
ZagComponenttsx
import * as myWidget from '@zag-js/my-widget'
import { normalizeProps } from '@zag-js/vanilla'
import { ZagComponent } from '@geajs/ui'
import type { SpreadMap } from '@geajs/ui'
export default class MyWidget extends ZagComponent {
declare open: boolean
createMachine() { return myWidget.machine }
getMachineProps(props: any) {
return {
id: this.id,
// map Gea props → Zag machine props
onOpenChange: (d: any) => { this.open = d.open; props.onOpenChange?.(d) },
}
}
connectApi(service: any) {
return myWidget.connect(service, normalizeProps)
}
getSpreadMap(): SpreadMap {
return {
'[data-part="root"]': 'getRootProps',
'[data-part="trigger"]': 'getTriggerProps',
'[data-part="content"]': 'getContentProps',
}
}
syncState(api: any) { this.open = api.open }
template(props: any) {
return (
<div data-part="root">
<button data-part="trigger">Toggle</button>
<div data-part="content">{props.children}</div>
</div>
)
}
}The maps CSS selectors to Zag API getter names (strings) or functions . After each render, applies Zag's ARIA/event attributes to matching DOM elements via .
SpreadMap(api, el) => propsZagComponentspreadProps如果要封装新的 Zag.js 状态机,只需继承 并实现5个方法即可:
ZagComponenttsx
import * as myWidget from '@zag-js/my-widget'
import { normalizeProps } from '@zag-js/vanilla'
import { ZagComponent } from '@geajs/ui'
import type { SpreadMap } from '@geajs/ui'
export default class MyWidget extends ZagComponent {
declare open: boolean
createMachine() { return myWidget.machine }
getMachineProps(props: any) {
return {
id: this.id,
// 将 Gea props 映射为 Zag 状态机 props
onOpenChange: (d: any) => { this.open = d.open; props.onOpenChange?.(d) },
}
}
connectApi(service: any) {
return myWidget.connect(service, normalizeProps)
}
getSpreadMap(): SpreadMap {
return {
'[data-part="root"]': 'getRootProps',
'[data-part="trigger"]': 'getTriggerProps',
'[data-part="content"]': 'getContentProps',
}
}
syncState(api: any) { this.open = api.open }
template(props: any) {
return (
<div data-part="root">
<button data-part="trigger">Toggle</button>
<div data-part="content">{props.children}</div>
</div>
)
}
}SpreadMap(api, el) => propsZagComponentspreadPropscn
Utility
cncn
工具函数
cnMerge Tailwind classes (powered by + ):
clsxtailwind-mergets
import { cn } from '@geajs/ui'
const cls = cn('px-4 py-2', active && 'bg-primary', className)用于合并 Tailwind 类名(基于 + 实现):
clsxtailwind-mergets
import { cn } from '@geajs/ui'
const cls = cn('px-4 py-2', active && 'bg-primary', className)