iter(g136): 新增链接组件 link

This commit is contained in:
HIsheR
2026-10-26 15:28:20 +08:00
parent b1bc8c6ed0
commit f6706c1622
13 changed files with 264 additions and 10 deletions
+1
View File
@@ -65,3 +65,4 @@ export { grid } from './grid.js';
export { floatButton } from './floatButton.js';
export { mentions } from './mentions.js';
export { treeSelect } from './treeSelect.js';
export { link } from './link.js';
+25
View File
@@ -0,0 +1,25 @@
import { h } from '@ui-arch/frame';
import type { LinkProps } from './types.js';
/*** 链接:站内跳转与站外跳转统一入口;external 自动补 ↗ 与 rel,disabled 去掉 href 并拦点击 ***/
export function link(props: LinkProps): HTMLElement {
const external = Boolean(props.external) || props.target === '_blank'; // 新窗口一律按站外处理
const cls = ['ua-link', props.muted ? 'is-muted' : '', props.disabled ? 'is-disabled' : '', external ? 'is-external' : '']
.filter(Boolean).join(' ');
const a = h('a', {
class: cls,
href: props.disabled ? null : props.href, // 禁用态去掉 href:键盘也不再当链接用
target: external ? '_blank' : props.target ?? null,
rel: external ? 'noopener noreferrer' : null, // 新窗口必须断开 opener,防目标页反向操控
'aria-disabled': props.disabled ? 'true' : null,
onclick: (e: Event) => {
if (props.disabled) {
e.preventDefault(); // 保留可聚焦但不可跳转
return;
}
props.onClick?.(e);
},
}, props.label, external ? h('span', { class: 'ua-link-icon', 'aria-hidden': 'true' }, '↗') : null);
if (external) a.append(h('span', { class: 'ua-visually-hidden' }, '(在新窗口打开)')); // 读屏需知道开了新窗
return a;
}
+11
View File
@@ -809,4 +809,15 @@ export interface TreeSelectProps {
onChange?: (keys: string[]) => void; // 多选勾选变更回调(传副本,父子已级联)
}
/*** 链接入参 ***/
export interface LinkProps {
href: string; // 目标地址;站内相对路径或绝对 URL
label: string; // 链接文案
target?: '_self' | '_blank' | '_parent' | '_top'; // 打开方式;缺省同窗口
external?: boolean; // 站外链接:补 ↗ 图标、强制新窗口并加 rel=noopener noreferrer
muted?: boolean; // 弱化色(次要链接 / 页脚);缺省主色
disabled?: boolean; // 禁用:去掉 href、拦点击,保留可聚焦与 aria-disabled
onClick?: (e: Event) => void; // 点击回调(禁用时不触发)
}
export type { ColumnDef, RowKey }; // 再导出常用内核类型,方便调用方