iter: d03 新增折叠面板 accordion 组件

This commit is contained in:
ui-arch-bot
2026-10-23 16:47:56 +08:00
parent 6b9fa95d1e
commit df2fb34e63
11 changed files with 281 additions and 8 deletions
+55
View File
@@ -0,0 +1,55 @@
import { h, signal } from '@ui-arch/frame';
import type { AccordionItem, AccordionProps } from './types.js';
/*** 折叠面板:标题按钮 + grid 行高过渡;单开(手风琴)或多开,展开态由 active 信号驱动或内部持有 ***/
export function accordion(props: AccordionProps): HTMLElement {
// —— 状态集中声明 ——
const internal = signal<string[]>([]); // 内部展开集合(未传 active 时使用)
const opened = (v: string): boolean => (props.active ? props.active() === v : internal().includes(v));
const toggle = (item: AccordionItem): void => {
if (props.active) {
const next = props.active() === item.value ? '' : item.value; // 单开:再点收起
props.active(next);
props.onChange?.(next);
return;
}
const cur = internal();
const next = cur.includes(item.value)
? cur.filter((v) => v !== item.value)
: props.multiple
? [...cur, item.value]
: [item.value];
internal(next);
props.onChange?.(next[0] ?? '');
};
// —— 面板:标题为原生 button(键盘可达),内容包一层 overflow 容器供过渡裁剪 ——
const rows = props.items.map((item, i) =>
h(
'div',
{ class: () => `ua-acc-item${opened(item.value) ? ' ua-open' : ''}` },
h(
'button',
{
type: 'button',
class: 'ua-acc-head',
id: `ua-acc-h-${i}`,
'aria-expanded': () => String(opened(item.value)),
'aria-controls': `ua-acc-b-${i}`,
disabled: item.disabled ?? false, // 布尔透传即得禁用态
onClick: () => toggle(item),
},
h('span', { class: 'ua-acc-title' }, item.title),
h('span', { class: 'ua-acc-caret' }, '▸'),
),
h(
'div',
{ class: 'ua-acc-body', id: `ua-acc-b-${i}`, role: 'region', 'aria-labelledby': `ua-acc-h-${i}` },
h('div', { class: 'ua-acc-inner' }, item.content),
),
),
);
return h('div', { class: 'ua-accordion' }, ...rows);
}
+1
View File
@@ -7,6 +7,7 @@ export { switchButton } from './switch.js';
export { select } from './select.js';
export { menu } from './menu.js';
export { radioGroup } from './radioGroup.js';
export { accordion } from './accordion.js';
export { card } from './card.js';
export { badge } from './badge.js';
export { alert } from './alert.js';
+16
View File
@@ -97,6 +97,22 @@ export interface MenuProps {
onSelect?: (value: string) => void; // 选中回调(禁用项不触发)
}
/*** 折叠面板条目 ***/
export interface AccordionItem {
value: string; // 面板标识(回调与展开集合用)
title: string; // 标题文案(同时作为触发器)
content: unknown; // 面板内容:字符串 / DOM 节点 / 响应式函数
disabled?: boolean; // 禁用该面板:标题置灰且不可展开
}
/*** 折叠面板入参 ***/
export interface AccordionProps {
items: AccordionItem[]; // 面板列表
active?: Signal<string>; // 展开面板信号(单开模式);缺省由组件内部持有状态
multiple?: boolean; // 是否允许多个面板同时展开;缺省 false(手风琴)
onChange?: (active: string) => void; // 展开变化回调:收起时传空串
}
/*** 多行输入入参 ***/
export interface TextareaProps {
placeholder?: string; // 占位文案