diff --git a/docs/components/home-page.tsx b/docs/components/home-page.tsx index 37722cd..04a9080 100644 --- a/docs/components/home-page.tsx +++ b/docs/components/home-page.tsx @@ -3,7 +3,7 @@ import { HomeLayout } from "fumadocs-ui/layouts/home"; import { useRouter } from "next/navigation"; -import { createElement, useEffect } from "react"; +import { createElement } from "react"; import { Button } from "@oreui-web/react/button"; import chevronRightUrl from "oreui-web/icons/chevron-right"; @@ -36,15 +36,12 @@ export function OreHomePage({ lang }: HomePageProps) { : "Made with ❤ by Minecraft enthusiasts."; const scrollLabel = lang === "zh-CN" ? "OreUI 首页" : "OreUI home page"; - useEffect(() => { - void import("oreui-web/scrollbar"); - }, []); - return createElement( - "ore-scrollbar", + "div", { - className: "ore-home-scrollbar h-dvh w-full [--ore-scrollbar-z-index:50]", + className: "ore-scrollbar ore-home-scrollbar h-dvh w-full", "aria-label": scrollLabel, + tabIndex: 0, }, <> @@ -57,7 +54,7 @@ export function OreHomePage({ lang }: HomePageProps) { className="inline-flex min-h-6 items-center gap-2 px-3 rounded-full bg-fd-secondary text-xs no-underline" href="https://github.com/katorlys/OreUI/releases" > - Version {version} out now + Version {version} { @@ -147,10 +148,7 @@ export function HomeShowcase() { max={16} aria-label="Render distance" onInput={(event) => - setDistance( - (event.currentTarget as HTMLElement & { value: number }) - .value, - ) + setDistance(event.currentTarget.valueAsNumber) } /> - setDistance( - (event.currentTarget as HTMLElement & { value: number }) - .value, - ) + setDistance(event.currentTarget.valueAsNumber) } />
+ + {progress}% +
setUsername(event.currentTarget.value)} /> { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } - return ( - - + + {title} - +
{content}
); diff --git a/docs/components/stories/button.story.tsx b/docs/components/stories/button.story.tsx index 00df8a8..8d2a0c0 100644 --- a/docs/components/stories/button.story.tsx +++ b/docs/components/stories/button.story.tsx @@ -49,13 +49,14 @@ function ButtonPreview({ export function LinkButtonPreview() { return ( - + ); } diff --git a/docs/components/stories/checkbox.story.tsx b/docs/components/stories/checkbox.story.tsx index 1853404..be0394e 100644 --- a/docs/components/stories/checkbox.story.tsx +++ b/docs/components/stories/checkbox.story.tsx @@ -41,6 +41,7 @@ function CheckboxPreview({
+

{description}

@@ -109,50 +88,22 @@ function ModalPreview({ description, title, triggerLabel }: ModalPreviewProps) { style={{ width: "min(100%, 15.25rem)" }} /> - - + + Checkbox example for confirmation - - - - - + + ); } diff --git a/docs/components/stories/progress-bar.story.tsx b/docs/components/stories/progress-bar.story.tsx index 76d428f..ab41074 100644 --- a/docs/components/stories/progress-bar.story.tsx +++ b/docs/components/stories/progress-bar.story.tsx @@ -9,12 +9,27 @@ interface ProgressBarPreviewProps { labelPosition: "bottom" | "top"; max: number; value: number; - variant: "labeled" | "plain"; } -function ProgressBarPreview(props: ProgressBarPreviewProps) { +function ProgressBarPreview({ + label, + labelAlign, + labelPosition, + max, + value, +}: ProgressBarPreviewProps) { + const output = label ? ( + + {label} + + ) : null; + return ( - +
+ {labelPosition === "top" ? output : null} + + {labelPosition === "bottom" ? output : null} +
); } @@ -30,7 +45,6 @@ export const progressBarStory = defineStory({ labelPosition: "bottom", max: 100, value: 65, - variant: "labeled", }, }, }); diff --git a/docs/components/stories/progress-ring.story.tsx b/docs/components/stories/progress-ring.story.tsx index 2abe648..1c03b41 100644 --- a/docs/components/stories/progress-ring.story.tsx +++ b/docs/components/stories/progress-ring.story.tsx @@ -2,7 +2,6 @@ import { defineStoryFactory } from "@fumadocs/story/next/client"; import { ProgressRing } from "@oreui-web/react/progress-ring"; -import { useEffect, useState } from "react"; interface ProgressRingPreviewProps { label: string; @@ -17,19 +16,20 @@ function ProgressRingPreview({ size, value, }: ProgressRingPreviewProps) { - const [mounted, setMounted] = useState(false); - - useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } + const safeMax = Number.isFinite(max) && max > 0 ? max : 100; + const safeValue = Math.min(safeMax, Math.max(0, value)); + const frame = Math.min(15, Math.floor((safeValue / safeMax) * 16)); return ( - + ); } diff --git a/docs/components/stories/radio-group.story.tsx b/docs/components/stories/radio-group.story.tsx index 6210490..2a8b636 100644 --- a/docs/components/stories/radio-group.story.tsx +++ b/docs/components/stories/radio-group.story.tsx @@ -38,12 +38,14 @@ function RadioGroupPreview({ return (
- + + {label} {options.map((option) => { const optionValue = option.toLowerCase(); return ( + + + Selected: {value || "none"} +
+ ); +} + +const { defineStory } = defineStoryFactory(); + +export const selectStory = defineStory({ + Component: SelectPreview, + displayName: "Select", + args: { + initial: { + disabled: false, + initialValue: "creative", + required: false, + }, + }, +}); + +export const SelectStory = selectStory.WithControl; diff --git a/docs/components/stories/slider.story.tsx b/docs/components/stories/slider.story.tsx index 25b7bb6..cb31e8d 100644 --- a/docs/components/stories/slider.story.tsx +++ b/docs/components/stories/slider.story.tsx @@ -15,11 +15,9 @@ interface SliderPreviewProps { | "gold"; disabled: boolean; initialValue: number; - initialValueStart: number; max: number; min: number; orientation: "horizontal" | "vertical"; - range: boolean; step: number; variant: "default" | "segmented"; } @@ -28,17 +26,14 @@ function SliderPreview({ color, disabled, initialValue, - initialValueStart, max, min, orientation, - range, step, variant, }: SliderPreviewProps) { const [mounted, setMounted] = useState(false); const [value, setValue] = useState(initialValue); - const [valueStart, setValueStart] = useState(initialValueStart); useEffect(() => { setMounted(true); @@ -57,23 +52,14 @@ function SliderPreview({ max={max} min={min} orientation={orientation} - range={range} step={step} value={value} - valueStart={valueStart} variant={variant} onInput={(event) => { - const slider = event.target as HTMLElement & { - value: number; - valueStart: number; - }; - setValue(slider.value); - setValueStart(slider.valueStart); + setValue((event.currentTarget as HTMLInputElement).valueAsNumber); }} /> - - Render distance: {range ? `${valueStart}-${value}` : value} - + Render distance: {value}
); } @@ -88,11 +74,9 @@ export const sliderStory = defineStory({ color: "primary", disabled: false, initialValue: 12, - initialValueStart: 4, max: 32, min: 2, orientation: "horizontal", - range: false, step: 1, variant: "segmented", }, diff --git a/docs/components/stories/spinner.story.tsx b/docs/components/stories/spinner.story.tsx index acb683b..25aeee1 100644 --- a/docs/components/stories/spinner.story.tsx +++ b/docs/components/stories/spinner.story.tsx @@ -2,7 +2,6 @@ import { defineStoryFactory } from "@fumadocs/story/next/client"; import { Spinner } from "@oreui-web/react/spinner"; -import { useEffect, useState } from "react"; interface SpinnerPreviewProps { decorative: boolean; @@ -11,16 +10,6 @@ interface SpinnerPreviewProps { } function SpinnerPreview({ decorative, label, size }: SpinnerPreviewProps) { - const [mounted, setMounted] = useState(false); - - useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } - return ( { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } - return (
{ - const control = event.target as HTMLElement & { checked: boolean }; - setChecked(control.checked); + onChange={(event: ChangeEvent) => { + setChecked(event.currentTarget.checked); }} > {label} diff --git a/docs/components/stories/tab-bar.story.tsx b/docs/components/stories/tab-bar.story.tsx deleted file mode 100644 index e2d3e1f..0000000 --- a/docs/components/stories/tab-bar.story.tsx +++ /dev/null @@ -1,55 +0,0 @@ -"use client"; - -import { defineStoryFactory } from "@fumadocs/story/next/client"; -import { TabBar, TabButton } from "@oreui-web/react/tab-bar"; -import { useState } from "react"; - -interface TabBarPreviewProps { - disabledLastTab: boolean; - label: string; - palette: "default" | "toggle"; -} - -function TabBarPreview({ - disabledLastTab, - label, - palette, -}: TabBarPreviewProps) { - const [selected, setSelected] = useState("worlds"); - - return ( - - {[ - ["worlds", "Worlds"], - ["realms", "Realms"], - ["discover", "Discover"], - ].map(([value, text], index) => ( - setSelected(value)} - > - {text} - - ))} - - ); -} - -const { defineStory } = defineStoryFactory(); - -export const tabBarStory = defineStory({ - Component: TabBarPreview, - displayName: "Tab Bar", - args: { - initial: { - disabledLastTab: false, - label: "Play menu", - palette: "default", - }, - }, -}); - -export const TabBarStory = tabBarStory.WithControl; diff --git a/docs/components/stories/textarea.story.tsx b/docs/components/stories/textarea.story.tsx index 0aca438..71206c0 100644 --- a/docs/components/stories/textarea.story.tsx +++ b/docs/components/stories/textarea.story.tsx @@ -2,7 +2,7 @@ import { defineStoryFactory } from "@fumadocs/story/next/client"; import { Textarea } from "@oreui-web/react/textarea"; -import { type CSSProperties, useEffect, useState } from "react"; +import { type CSSProperties, type FormEvent, useEffect, useState } from "react"; interface TextareaPreviewProps { description: string; @@ -10,7 +10,7 @@ interface TextareaPreviewProps { error: string; label: string; placeholder: string; - readonly: boolean; + readOnly: boolean; required: boolean; rows: number; spellcheck: boolean; @@ -23,7 +23,7 @@ function TextareaPreview({ error, label, placeholder, - readonly, + readOnly, required, rows, spellcheck, @@ -48,7 +48,7 @@ function TextareaPreview({ error={error} label={label} placeholder={placeholder} - readonly={readonly} + readOnly={readOnly} required={required} rows={rows} spellCheck={spellcheck} @@ -60,8 +60,8 @@ function TextareaPreview({ "--ore-textarea-foreground": "var(--color-fd-foreground)", } as CSSProperties } - onInput={(event) => { - const textarea = event.target as HTMLElement & { value: string }; + onInput={(event: FormEvent) => { + const textarea = event.currentTarget; setValue(textarea.value); }} /> @@ -82,7 +82,7 @@ export const textareaStory = defineStory({ error: "", label: "World description", placeholder: "Describe your world", - readonly: false, + readOnly: false, required: false, rows: 3, spellcheck: true, diff --git a/docs/components/stories/textfield.story.tsx b/docs/components/stories/textfield.story.tsx index 80afb9d..34a382c 100644 --- a/docs/components/stories/textfield.story.tsx +++ b/docs/components/stories/textfield.story.tsx @@ -14,6 +14,11 @@ interface TextfieldPreviewProps { type: string; } +const textfieldStyle = { + "--ore-textfield-description": "var(--color-fd-muted-foreground)", + "--ore-textfield-foreground": "var(--color-fd-foreground)", +} as CSSProperties; + function TextfieldPreview({ description, disabled, @@ -35,28 +40,104 @@ function TextfieldPreview({ } return ( -
- { - const field = event.target as HTMLElement & { value: string }; - setValue(field.value); +
+
- Value: {value || "empty"} + > +
+

default

+ +
+
+

hover

+ +
+
+

focus

+ +
+
+

pressed

+ +
+
+

disabled

+ +
+
+ +
+ { + const field = event.currentTarget as HTMLInputElement; + setValue(field.value); + }} + /> + +
setValue("")} + onSubmit={(event) => event.preventDefault()} + style={{ display: "flex", gap: "0.5rem", flexWrap: "wrap" }} + > + + + Value: {value || "empty"} +
+
); } diff --git a/docs/components/stories/toggles.story.tsx b/docs/components/stories/toggles.story.tsx index f9ee628..814cb81 100644 --- a/docs/components/stories/toggles.story.tsx +++ b/docs/components/stories/toggles.story.tsx @@ -2,6 +2,7 @@ import { defineStoryFactory } from "@fumadocs/story/next/client"; import { TabButton, Toggles } from "@oreui-web/react/toggles"; +import { initTabButtons } from "oreui-web"; import { useEffect, useState } from "react"; interface TogglesPreviewProps { @@ -19,6 +20,12 @@ function TogglesPreview({ disabledOption, initialValue }: TogglesPreviewProps) { setMounted(true); }, []); + useEffect(() => { + if (mounted) { + initTabButtons(); + } + }, [mounted]); + if (!mounted) { return null; } @@ -33,7 +40,7 @@ function TogglesPreview({ disabledOption, initialValue }: TogglesPreviewProps) { key={optionValue} disabled={disabledOption && optionValue === "always"} selected={value === optionValue} - onChange={() => setValue(optionValue)} + onClick={() => setValue(optionValue)} > {option} diff --git a/docs/content/docs/buttons/button.mdx b/docs/content/docs/buttons/button.mdx index 0940d1c..b1c96e1 100644 --- a/docs/content/docs/buttons/button.mdx +++ b/docs/content/docs/buttons/button.mdx @@ -18,11 +18,11 @@ Use buttons for actions such as confirming a choice, opening a screen, or submit ```js -import "oreui-web/button"; +import "oreui-web/styles.css"; ``` ```html -Create new world + ``` @@ -75,11 +75,7 @@ export function App() { ```astro -Create new world - - + ``` @@ -89,34 +85,36 @@ export function App() { ## Variants ```html -Default -Hero + + ``` ## Colors ```html -Primary -Secondary -Delete -Dungeons -Legends -Gold -Realms + + + + + + + ``` ## Disabled ```html -Unavailable + ``` ## Loading -Set `loading` while an action is in progress. The button keeps its width, shows a spinner, exposes `aria-busy="true"`, and prevents repeated activation. +Set `data-loading` while an action is in progress, and keep the native button disabled with `aria-busy="true"`. Framework bindings do this mapping when their `loading` prop is set. ```html -Saving + ``` ## Prefix and suffix @@ -126,30 +124,35 @@ Use the public `.ore-button-prefix` and `.ore-button-suffix` classes to place pr ```html - + - + ``` ## Links -Set `href` to use the component for navigation. Link buttons use a native anchor and support browser behaviors such as opening in a new tab, copying the link, and downloading a resource. +Use a native anchor with `class="ore-button"` for navigation. It preserves browser behaviors such as opening in a new tab, copying the link, and downloading a resource. ```html - + Open website - + ``` -Use a link button for navigation and a regular button for actions. A disabled link is removed from the tab order and does not navigate. +Use a link button for navigation and a regular button for actions. Anchors do not support `disabled`; omit `href` or remove the link when navigation is unavailable. ## Forms @@ -157,32 +160,18 @@ The default `type` is `button`. Set `type="submit"` explicitly for a submit butt ```html
- Save - Reset + +
- + ``` ## API -| Property | Attribute | Type | Default | -| ---------- | ---------- | -------------------------------------------------------------------------------------------- | ----------- | -| `color` | `color` | `"destructive" \| "dungeons" \| "gold" \| "legends" \| "primary" \| "realms" \| "secondary"` | `"primary"` | -| `disabled` | `disabled` | `boolean` | `false` | -| `download` | `download` | `string \| undefined` | `undefined` | -| `href` | `href` | `string \| undefined` | `undefined` | -| `loading` | `loading` | `boolean` | `false` | -| `name` | `name` | `string` | `""` | -| `rel` | `rel` | `string \| undefined` | `undefined` | -| `target` | `target` | `string \| undefined` | `undefined` | -| `type` | `type` | `"button" \| "reset" \| "submit"` | `"button"` | -| `value` | `value` | `string` | `""` | -| `variant` | `variant` | `"default" \| "hero"` | `"default"` | - -The read-only `form` property returns the associated `HTMLFormElement`, or `null`. The `form` attribute accepts a form ID. +Button uses the native `HTMLButtonElement` API. Use native attributes such as `disabled`, `form`, `name`, `type`, and `value`; use `data-color`, `data-variant`, and `data-loading` only for OreUI visual configuration. ## CSS variables @@ -204,4 +193,4 @@ The read-only `form` property returns the associated `HTMLFormElement`, or `null ## Accessibility -Without `href`, the component uses button semantics and supports `Enter` and `Space`. With `href`, a native anchor provides link semantics and standard browser navigation. Disabled buttons and links are removed from the tab order and expose `aria-disabled="true"`. Loading buttons expose `aria-busy="true"` and prevent repeated activation. +Native buttons provide form participation, validation, focus, `Enter`, and `Space` behavior without a controller. Native anchors provide standard link semantics and navigation. Loading buttons remain disabled and expose `aria-busy="true"`. diff --git a/docs/content/docs/buttons/button.zh-CN.mdx b/docs/content/docs/buttons/button.zh-CN.mdx index af660ea..60b83b6 100644 --- a/docs/content/docs/buttons/button.zh-CN.mdx +++ b/docs/content/docs/buttons/button.zh-CN.mdx @@ -18,11 +18,11 @@ Button 用于确认选择、打开页面、提交表单等操作。 ```js -import "oreui-web/button"; +import "oreui-web/styles.css"; ``` ```html -Create new world + ``` @@ -75,11 +75,7 @@ export function App() { ```astro -Create new world - - + ``` @@ -89,34 +85,36 @@ export function App() { ## 变体 ```html -Default -Hero + + ``` ## 颜色 ```html -Primary -Secondary -Delete -Dungeons -Legends -Gold -Realms + + + + + + + ``` ## Disabled ```html -Unavailable + ``` ## 加载中 -操作进行时可设置 `loading`。按钮会保持原宽度、显示 Spinner、提供 `aria-busy="true"`,并阻止重复触发。 +操作进行时设置 `data-loading`,并同时保留原生 `disabled` 和 `aria-busy="true"`。框架 wrapper 会在设置 `loading` prop 时完成这些映射。 ```html -Saving + ``` ## 前后缀 @@ -126,30 +124,35 @@ export function App() { ```html - + - + ``` ## 链接 -设置 `href` 后,组件可用于页面导航。链接按钮使用原生锚点,支持在新标签页打开、复制链接和下载资源等浏览器行为。 +页面导航使用带 `class="ore-button"` 的原生锚点,并保留在新标签页打开、复制链接和下载资源等浏览器行为。 ```html - + Open website - + ``` -页面导航应使用链接按钮,触发操作则使用普通按钮。禁用的链接会移出 Tab 顺序,并且不会发生导航。 +页面导航应使用链接按钮,触发操作则使用普通按钮。锚点不支持 `disabled`;导航不可用时应移除 `href` 或移除该链接。 ## 表单 @@ -157,32 +160,18 @@ export function App() { ```html
- Save - Reset + +
- + ``` ## API -| Prop | 属性 | 类型 | 默认值 | -| ---------- | ---------- | -------------------------------------------------------------------------------------------- | ----------- | -| `color` | `color` | `"destructive" \| "dungeons" \| "gold" \| "legends" \| "primary" \| "realms" \| "secondary"` | `"primary"` | -| `disabled` | `disabled` | `boolean` | `false` | -| `download` | `download` | `string \| undefined` | `undefined` | -| `href` | `href` | `string \| undefined` | `undefined` | -| `loading` | `loading` | `boolean` | `false` | -| `name` | `name` | `string` | `""` | -| `rel` | `rel` | `string \| undefined` | `undefined` | -| `target` | `target` | `string \| undefined` | `undefined` | -| `type` | `type` | `"button" \| "reset" \| "submit"` | `"button"` | -| `value` | `value` | `string` | `""` | -| `variant` | `variant` | `"default" \| "hero"` | `"default"` | - -只读的 `form` property 会返回关联的 `HTMLFormElement`,没有关联表单时返回 `null`。`form` 属性接受表单 ID。 +Button 直接使用原生 `HTMLButtonElement` API。`disabled`、`form`、`name`、`type`、`value` 等使用原生属性;只有 OreUI 视觉配置使用 `data-color`、`data-variant` 和 `data-loading`。 ## CSS 变量 @@ -204,4 +193,4 @@ export function App() { ## 无障碍 -未设置 `href` 时,组件使用按钮语义,并支持 `Enter` 和 `Space` 键。设置 `href` 后,原生锚点会提供链接语义和标准浏览器导航行为。禁用的按钮和链接不会被 Tab 键选中,并带有 `aria-disabled="true"`。加载中的按钮提供 `aria-busy="true"` 并阻止重复触发。 +原生按钮无需 controller 即可提供表单参与、校验、焦点、`Enter` 和 `Space` 行为。原生锚点提供标准链接语义和导航。加载中的按钮保持禁用,并提供 `aria-busy="true"`。 diff --git a/docs/content/docs/buttons/icon-button.mdx b/docs/content/docs/buttons/icon-button.mdx index 4b1c412..29989bd 100644 --- a/docs/content/docs/buttons/icon-button.mdx +++ b/docs/content/docs/buttons/icon-button.mdx @@ -12,12 +12,12 @@ import { IconButtonStory } from "@/components/stories/icon-button.story"; ```js -import "oreui-web/icon-button"; import "oreui-web/icon"; +import "oreui-web/styles.css"; ``` ```html - + ``` @@ -125,13 +125,13 @@ export function CloseButton() { ```astro - + ``` @@ -142,7 +142,7 @@ export function CloseButton() { ## Disabled ```html - + ``` ## API -Icon Button inherits the Button API. +Icon Button is a native button. It supports standard button attributes, events, form behavior, and focus behavior. | Property | Attribute | Type | Default | | ---------- | ---------- | ------------------------------- | ---------- | diff --git a/docs/content/docs/buttons/icon-button.zh-CN.mdx b/docs/content/docs/buttons/icon-button.zh-CN.mdx index 1f4c1a8..e688d01 100644 --- a/docs/content/docs/buttons/icon-button.zh-CN.mdx +++ b/docs/content/docs/buttons/icon-button.zh-CN.mdx @@ -12,12 +12,12 @@ import { IconButtonStory } from "@/components/stories/icon-button.story"; ```js -import "oreui-web/icon-button"; import "oreui-web/icon"; +import "oreui-web/styles.css"; ``` ```html - > - + ``` @@ -117,13 +117,13 @@ export function CloseButton() { ```astro - + ``` @@ -134,7 +134,7 @@ export function CloseButton() { ## Disabled ```html - + ``` ## API -Icon Button 继承 Button 的 API。 +Icon Button 是原生按钮,支持标准按钮属性、事件、表单行为和焦点行为。 | Prop | 属性 | 类型 | 默认值 | | ---------- | ---------- | ------------------------------- | ---------- | diff --git a/docs/content/docs/buttons/tab-button.mdx b/docs/content/docs/buttons/tab-button.mdx index 906cabd..d68b379 100644 --- a/docs/content/docs/buttons/tab-button.mdx +++ b/docs/content/docs/buttons/tab-button.mdx @@ -7,13 +7,27 @@ This should only be used in Tab Bar components, which makes selection and keyboa ## Usage -See [Tab Bar](../../navigation/tab-bar). +Place tab buttons inside any element with `role="tablist"`. ## Palettes ```html -Worlds -Enabled + + ``` ## Events @@ -21,18 +35,20 @@ See [Tab Bar](../../navigation/tab-bar). Tab Button dispatches `change` after it becomes selected. ```js -const tab = document.querySelector("ore-tab-button"); -tab?.addEventListener("change", () => console.log(tab.selected)); +const tab = document.querySelector(".ore-tab-button"); +tab?.addEventListener("change", () => + console.log(tab.getAttribute("aria-selected")), +); ``` ## API -| Property | Attribute | Type | Default | -| ---------- | ---------- | ------------------------------- | ----------- | -| `disabled` | `disabled` | `boolean` | `false` | -| `palette` | `palette` | `"default" \|"toggle"` | `"default"` | -| `selected` | `selected` | `boolean` | `false` | -| `type` | `type` | `"button" \|"reset" \|"submit"` | `"button"` | +| Property | Attribute | Type | Default | +| ---------- | --------------- | ------------------------------- | ----------- | +| `disabled` | `disabled` | `boolean` | `false` | +| `palette` | `data-palette` | `"default" \|"toggle"` | `"default"` | +| `selected` | `aria-selected` | `"true" \|"false"` | `"false"` | +| `type` | `type` | `"button" \|"reset" \|"submit"` | `"button"` | | Event | Detail | | -------- | ------ | @@ -59,4 +75,4 @@ Tab Button also accepts all [Button CSS variables](../button#css-variables). ## Accessibility -Tab Button uses `role="tab"` and updates `aria-selected`. `Arrow keys` move through tabs that are not disabled, while `Home` and `End` select the first and last tab. +Tab Button is a native `button` with `role="tab"`. The Tab Bar controller updates `aria-selected`. `Arrow keys` move through tabs that are not disabled, while `Home` and `End` select the first and last tab. diff --git a/docs/content/docs/buttons/tab-button.zh-CN.mdx b/docs/content/docs/buttons/tab-button.zh-CN.mdx index c26e3ae..46fcd8c 100644 --- a/docs/content/docs/buttons/tab-button.zh-CN.mdx +++ b/docs/content/docs/buttons/tab-button.zh-CN.mdx @@ -7,13 +7,27 @@ Tab Button 只能用于 Tab Bar 组件中,由 Tab Bar 统一处理选中状态 ## 用法 -见 [Tab Bar](../../navigation/tab-bar)。 +将 Tab Button 放在带有 `role="tablist"` 的任意元素中即可组成选项卡列表。 ## 颜色 ```html -Worlds -Enabled + + ``` ## 事件 @@ -21,18 +35,20 @@ Tab Button 只能用于 Tab Bar 组件中,由 Tab Bar 统一处理选中状态 Tab Button 在变为选中状态后触发 `change` 事件。 ```js -const tab = document.querySelector("ore-tab-button"); -tab?.addEventListener("change", () => console.log(tab.selected)); +const tab = document.querySelector(".ore-tab-button"); +tab?.addEventListener("change", () => + console.log(tab.getAttribute("aria-selected")), +); ``` ## API -| Prop | 属性 | 类型 | 默认值 | -| ---------- | ---------- | ------------------------------- | ----------- | -| `disabled` | `disabled` | `boolean` | `false` | -| `palette` | `palette` | `"default" \|"toggle"` | `"default"` | -| `selected` | `selected` | `boolean` | `false` | -| `type` | `type` | `"button" \|"reset" \|"submit"` | `"button"` | +| Prop | 属性 | 类型 | 默认值 | +| ---------- | --------------- | ------------------------------- | ----------- | +| `disabled` | `disabled` | `boolean` | `false` | +| `palette` | `data-palette` | `"default" \|"toggle"` | `"default"` | +| `selected` | `aria-selected` | `"true" \|"false"` | `"false"` | +| `type` | `type` | `"button" \|"reset" \|"submit"` | `"button"` | | 事件 | 详情 | | -------- | ---- | @@ -59,4 +75,4 @@ Tab Button 也可使用所有的 [Button CSS 变量](../button#css-变量)。 ## 无障碍 -Tab Button 使用 `role="tab"` 并更新 `aria-selected`。`方向键`可在非 disabled 状态的标签之间移动;`Home` 和 `End` 键分别选择第一个和最后一个标签。 +Tab Button 使用原生 `button` 和 `role="tab"`。Tab Bar controller 负责更新 `aria-selected`。`方向键`可在非 disabled 状态的标签之间移动。`Home` 和 `End` 键分别选择第一个和最后一个标签。 diff --git a/docs/content/docs/controls/checkbox.mdx b/docs/content/docs/controls/checkbox.mdx index 449a9a0..c3136ce 100644 --- a/docs/content/docs/controls/checkbox.mdx +++ b/docs/content/docs/controls/checkbox.mdx @@ -12,11 +12,14 @@ import { CheckboxStory } from "@/components/stories/checkbox.story"; ```js -import "oreui-web/checkbox"; +import "oreui-web/styles.css"; ``` ```html - Receive updates + ``` @@ -35,8 +38,7 @@ export function UpdatesCheckbox() { name="updates" value="yes" onChange={(event) => { - const checkbox = event.target as HTMLElement & { checked: boolean }; - setChecked(checkbox.checked); + setChecked(event.currentTarget.checked); }} > Receive updates @@ -105,13 +107,14 @@ export function UpdatesCheckbox() { ```astro - - Receive updates - + - + ``` @@ -121,21 +124,75 @@ export function UpdatesCheckbox() { ## Colors ```html -Primary -Secondary -Destructive -Dungeons -Legends -Realms -Gold + + + + + + + ``` ## States ```html -Enabled -Unavailable -Required agreement + + + ``` ## Forms @@ -144,33 +201,28 @@ Checked values are included in `FormData`. Reset restores the initial `checked` ```html
- + - Continue - Reset + + +
``` ## Events -Checkbox dispatches native `input` and `change` events after its value changes. +The native checkbox dispatches `input` and `change` events when its value changes. ## API -| Property | Attribute | Type | Default | -| ---------- | ---------- | -------------------------------------------------------------------------------------------- | ----------- | -| `checked` | `checked` | `boolean` | `false` | -| `color` | `color` | `"primary" \| "secondary" \| "destructive" \| "dungeons" \| "legends" \| "realms" \| "gold"` | `"primary"` | -| `disabled` | `disabled` | `boolean` | `false` | -| `name` | `name` | `string` | `""` | -| `required` | `required` | `boolean` | `false` | -| `value` | `value` | `string` | `"on"` | - -| Method | Description | -| ------------------ | ---------------------------------- | -| `checkValidity()` | Check the current validity | -| `reportValidity()` | Check validity and report an error | +Checkbox uses the native `HTMLInputElement` API. Use `checked`, `disabled`, `name`, `required`, and `value` directly; use `data-color` only for the OreUI palette. Native methods such as `checkValidity()` and `reportValidity()` remain available. ## CSS variables @@ -190,4 +242,4 @@ Checkbox dispatches native `input` and `change` events after its value changes. ## Accessibility -Checkbox provides checkbox semantics and supports Space. +The native input provides checkbox semantics, label activation, Space key behavior, form participation, validation, and reset behavior without JavaScript. diff --git a/docs/content/docs/controls/checkbox.zh-CN.mdx b/docs/content/docs/controls/checkbox.zh-CN.mdx index d798b23..0544e86 100644 --- a/docs/content/docs/controls/checkbox.zh-CN.mdx +++ b/docs/content/docs/controls/checkbox.zh-CN.mdx @@ -12,11 +12,14 @@ import { CheckboxStory } from "@/components/stories/checkbox.story"; ```js -import "oreui-web/checkbox"; +import "oreui-web/styles.css"; ``` ```html - Receive updates + ``` @@ -35,8 +38,7 @@ export function UpdatesCheckbox() { name="updates" value="yes" onChange={(event) => { - const checkbox = event.target as HTMLElement & { checked: boolean }; - setChecked(checkbox.checked); + setChecked(event.currentTarget.checked); }} > Receive updates @@ -104,13 +106,14 @@ export function UpdatesCheckbox() { ```astro - - Receive updates - + - + ``` @@ -120,21 +123,75 @@ export function UpdatesCheckbox() { ## 颜色 ```html -Primary -Secondary -Destructive -Dungeons -Legends -Realms -Gold + + + + + + + ``` ## 状态 ```html -Enabled -Unavailable -Required agreement + + + ``` ## 表单 @@ -143,33 +200,28 @@ export function UpdatesCheckbox() { ```html
- + - Continue - Reset + + +
``` ## 事件 -Checkbox 的值发生变化后,会触发原生 `input` 和 `change` 事件。 +原生 Checkbox 的值发生变化后会触发 `input` 和 `change` 事件。 ## API -| Prop | 属性 | 类型 | 默认值 | -| ---------- | ---------- | -------------------------------------------------------------------------------------------- | ----------- | -| `checked` | `checked` | `boolean` | `false` | -| `color` | `color` | `"primary" \| "secondary" \| "destructive" \| "dungeons" \| "legends" \| "realms" \| "gold"` | `"primary"` | -| `disabled` | `disabled` | `boolean` | `false` | -| `name` | `name` | `string` | `""` | -| `required` | `required` | `boolean` | `false` | -| `value` | `value` | `string` | `"on"` | - -| 方法 | 描述 | -| ------------------ | -------------------- | -| `checkValidity()` | 检查当前有效性 | -| `reportValidity()` | 检查有效性并报告错误 | +Checkbox 直接使用原生 `HTMLInputElement` API。`checked`、`disabled`、`name`、`required` 和 `value` 使用原生属性;只有 OreUI 配色使用 `data-color`。`checkValidity()`、`reportValidity()` 等原生方法保持可用。 ## CSS 变量 @@ -189,4 +241,4 @@ Checkbox 的值发生变化后,会触发原生 `input` 和 `change` 事件。 ## 无障碍 -Checkbox 提供相应的无障碍语义,并支持空格键。 +原生 input 无需 JavaScript 就能提供 Checkbox 语义、label 激活、空格键操作、表单参与、校验和重置行为。 diff --git a/docs/content/docs/controls/dropdown.mdx b/docs/content/docs/controls/dropdown.mdx index 61a8ae8..78f5a8b 100644 --- a/docs/content/docs/controls/dropdown.mdx +++ b/docs/content/docs/controls/dropdown.mdx @@ -12,15 +12,18 @@ import { DropdownStory } from "@/components/stories/dropdown.story"; ```js -import "oreui-web/button"; import "oreui-web/dropdown"; ``` ```html - - +
+
- +
```
@@ -188,19 +191,18 @@ export function GameModeDropdown() { ```astro - - +
+
- +
``` @@ -215,21 +217,19 @@ export function GameModeDropdown() { ## Events -| Event | Detail | -| ------------- | -------------------------------------- | -| `change` | `{ item: HTMLElement; value: string }` | -| `open-change` | `boolean` | +| Event | Detail | +| -------- | --------------------------------------------- | +| `change` | `{ item: HTMLButtonElement; value: string }` | +| `toggle` | Standard Popover event dispatched by the menu | -In React, use `onChange` and `onOpenChange`. +In React, use `onChange`. Listen for `toggle` on the menu when open state matters. ## API -| Property | Attribute | Type | Default | -| ------------- | -------------- | ---------------------------- | -------------- | -| `defaultOpen` | `default-open` | `boolean` | `false` | -| `open` | `open` | `boolean` | `false` | -| `value` | `value` | `string` | `""` | -| `variant` | `variant` | `"bordered" \| "borderless"` | `"borderless"` | +| Data attribute | Type | Default | +| -------------- | ---------------------------- | -------------- | +| `data-value` | `string` | `""` | +| `data-variant` | `"bordered" \| "borderless"` | `"borderless"` | ## Public classes diff --git a/docs/content/docs/controls/dropdown.zh-CN.mdx b/docs/content/docs/controls/dropdown.zh-CN.mdx index 9403993..bb972a4 100644 --- a/docs/content/docs/controls/dropdown.zh-CN.mdx +++ b/docs/content/docs/controls/dropdown.zh-CN.mdx @@ -12,15 +12,18 @@ import { DropdownStory } from "@/components/stories/dropdown.story"; ```js -import "oreui-web/button"; import "oreui-web/dropdown"; ``` ```html - - +
+
- +
```
@@ -186,19 +189,18 @@ export function GameModeDropdown() { ```astro - - +
+
- +
``` @@ -213,21 +215,19 @@ export function GameModeDropdown() { ## 事件 -| 事件 | 详情 | -| ------------- | -------------------------------------- | -| `change` | `{ item: HTMLElement; value: string }` | -| `open-change` | `boolean` | +| 事件 | 详情 | +| -------- | -------------------------------------------- | +| `change` | `{ item: HTMLButtonElement; value: string }` | +| `toggle` | 菜单派发的标准 Popover 事件 | -对于 React,请使用 `onChange` 和 `onOpenChange`。 +对于 React,请使用 `onChange`。需要打开状态时,在菜单上监听 `toggle`。 ## API -| Prop | 属性 | 类型 | 默认值 | -| ------------- | -------------- | ---------------------------- | -------------- | -| `defaultOpen` | `default-open` | `boolean` | `false` | -| `open` | `open` | `boolean` | `false` | -| `value` | `value` | `string` | `""` | -| `variant` | `variant` | `"bordered" \| "borderless"` | `"borderless"` | +| Data 属性 | 类型 | 默认值 | +| -------------- | ---------------------------- | -------------- | +| `data-value` | `string` | `""` | +| `data-variant` | `"bordered" \| "borderless"` | `"borderless"` | ## 公开类 diff --git a/docs/content/docs/controls/meta.json b/docs/content/docs/controls/meta.json index 66f6056..58dffa2 100644 --- a/docs/content/docs/controls/meta.json +++ b/docs/content/docs/controls/meta.json @@ -5,6 +5,7 @@ "dropdown", "radio", "radio-group", + "select", "slider", "switch", "textarea", diff --git a/docs/content/docs/controls/meta.zh-CN.json b/docs/content/docs/controls/meta.zh-CN.json index 30f42a2..619ecd8 100644 --- a/docs/content/docs/controls/meta.zh-CN.json +++ b/docs/content/docs/controls/meta.zh-CN.json @@ -5,6 +5,7 @@ "dropdown", "radio", "radio-group", + "select", "slider", "switch", "textarea", diff --git a/docs/content/docs/controls/radio-group.mdx b/docs/content/docs/controls/radio-group.mdx index 6196013..9714055 100644 --- a/docs/content/docs/controls/radio-group.mdx +++ b/docs/content/docs/controls/radio-group.mdx @@ -11,16 +11,22 @@ import { RadioGroupStory } from "@/components/stories/radio-group.story"; -```js -import "oreui-web/radio-group"; -``` - ```html - - Survival - Creative - Hardcore - +
+ World type + + + +
```
@@ -31,7 +37,8 @@ import { Radio, RadioGroup } from "@oreui-web/react/radio-group"; export function WorldType() { return ( - + + World type Survival @@ -55,7 +62,8 @@ import { Radio, RadioGroup } from "@oreui-web/vue";