iter: d09 新增滑块 slider 组件

This commit is contained in:
ui-arch-bot
2026-10-23 16:53:06 +08:00
parent 1a4b2c1fed
commit a11157b80a
11 changed files with 258 additions and 10 deletions
+1
View File
@@ -2,6 +2,7 @@ export * from './types.js';
export { button } from './button.js';
export { textField } from './textField.js';
export { textarea } from './textarea.js';
export { slider } from './slider.js';
export { checkbox } from './checkbox.js';
export { switchButton } from './switch.js';
export { select } from './select.js';
+32
View File
@@ -0,0 +1,32 @@
import { effect, h } from '@ui-arch/frame';
import type { SliderProps } from './types.js';
/*** 滑块:原生 range 承载交互与键盘,value 信号双向绑定;轨道配色走 accent-color 令牌 ***/
export function slider(props: SliderProps): HTMLElement {
const value = props.value; // 必需信号:组件不持有数值,一律委托调用方
const input = h('input', {
type: 'range',
class: 'ua-slider-input',
min: props.min ?? 0,
max: props.max ?? 100,
step: props.step ?? 1,
disabled: props.disabled, // 布尔或信号禁用,h() 直接透传
}) as HTMLInputElement;
input.addEventListener('input', () => {
const next = Number(input.value);
value(next); // 用户拖动写回信号
props.onChange?.(next);
});
effect(() => {
const next = String(value());
if (input.value !== next) input.value = next; // 外部赋值同步滑块位置(等值不重写,避免打断拖动)
});
return h(
'div',
{ class: 'ua-slider' },
input,
props.showValue === false ? null : h('span', { class: 'ua-slider-value' }, () => String(value())),
);
}
+11
View File
@@ -137,6 +137,17 @@ export interface AccordionProps {
onChange?: (active: string) => void; // 展开变化回调:收起时传空串
}
/*** 滑块入参 ***/
export interface SliderProps {
value: Signal<number>; // 当前值信号,双向绑定(拖动写回、外部赋值同步)
min?: number; // 最小值;缺省 0
max?: number; // 最大值;缺省 100
step?: number; // 步长;缺省 1
disabled?: boolean | Signal<boolean>; // 禁用态:布尔直接置位,传信号则动态同步
showValue?: boolean; // 是否右侧显示当前值;缺省 true
onChange?: (value: number) => void; // 拖动回调
}
/*** 多行输入入参 ***/
export interface TextareaProps {
placeholder?: string; // 占位文案
+61 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@ui-arch/ui",
"type": "skin-dom",
"description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / alert / toast / tooltip / popover / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
"description": "DOM 皮肤组件库:button / textField / textarea / slider / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / alert / toast / tooltip / popover / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
"tags": [
"components",
"dom",
@@ -16,6 +16,7 @@
"button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用",
"textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号禁用",
"textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号禁用",
"slider(props):滑块,value 信号双向绑定,min/max/step 可配,原生 range 承载键盘交互,右侧可显示当前值",
"checkbox(props):复选框,checked 信号双向绑定,disabled 支持布尔或信号禁用",
"switchButton(props):开关,checked 信号双向绑定,disabled 支持布尔或信号禁用",
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用",
@@ -70,7 +71,8 @@
"FormItemHandle",
"FormProps",
"DataTableProps",
"PopoverProps"
"PopoverProps",
"SliderProps"
]
},
"usage": "const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')",
@@ -80,7 +82,7 @@
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
],
"docs": {
"intro": "24 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
"intro": "25 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
"steps": [
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
@@ -199,6 +201,62 @@
"字数统计等派生逻辑直接在响应式文本里读 bio().length"
]
},
{
"id": "slider",
"title": "滑块 Slider",
"demo": "sec-slider",
"props": [
{
"n": "value",
"t": "Signal<number>",
"r": 1,
"d": "当前值信号,双向绑定:拖动写回,外部赋值同步位置"
},
{
"n": "min",
"t": "number",
"d": "最小值;缺省 0",
"df": "0"
},
{
"n": "max",
"t": "number",
"d": "最大值;缺省 100",
"df": "100"
},
{
"n": "step",
"t": "number",
"d": "步长;缺省 1",
"df": "1"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
"d": "禁用态:布尔直接置位,传信号则动态同步"
},
{
"n": "showValue",
"t": "boolean",
"d": "是否右侧显示当前值;缺省 true",
"df": "true"
},
{
"n": "onChange",
"t": "(value: number) => void",
"k": 1,
"d": "拖动回调"
}
],
"example": "const vol = signal(40); slider({ value: vol, min: 0, max: 100, step: 5, onChange: (v) => console.log(v) })",
"steps": [
"创建数值信号(初值在 min/max 区间内),传入 value 完成双向绑定",
"用 min / max / step 限定范围与粒度,如 step: 5 只取 5 的倍数",
"拖动或按 ←→ 键改变值:原生 range 自带键盘与触屏交互,无需额外处理",
"只想看拖动条不显示数字时传 showValue: false;需要自定义展示可自行读 signal",
"禁用场景传 disabled(布尔或信号),置灰且不可拖"
]
},
{
"id": "checkbox",
"title": "复选框 Checkbox",
+34
View File
@@ -539,6 +539,39 @@
padding-bottom: 12px;
}
/* —— 滑块 —— */
.ua-slider {
display: inline-flex;
align-items: center;
gap: var(--ua-space-3);
min-width: 180px;
}
.ua-slider-input {
flex: 1;
height: 4px;
margin: 0;
border-radius: var(--ua-radius-sm);
background: color-mix(in srgb, var(--ua-color-border) 70%, transparent);
accent-color: var(--ua-color-accent);
cursor: pointer;
transition: opacity 0.15s;
}
.ua-slider-input:focus-visible {
outline: none;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ua-color-accent) 20%, transparent);
}
.ua-slider-input:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.ua-slider-value {
min-width: 32px;
color: var(--ua-color-text-muted);
font-size: var(--ua-font-size-md);
font-variant-numeric: tabular-nums;
text-align: right;
}
/* —— 多行输入 —— */
.ua-textarea {
width: 100%;
@@ -1045,6 +1078,7 @@
.ua-acc-body,
.ua-crumb-link,
.ua-pop,
.ua-slider-input,
.ua-switch-track,
.ua-switch-thumb,
.ua-tab,