iter(g136): 新增链接组件 link
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 }; // 再导出常用内核类型,方便调用方
|
||||
|
||||
@@ -79,7 +79,8 @@
|
||||
"grid(props, ...children):栅格容器,columns 固定列数或 min 走 auto-fit 自适应列宽,gutter 统一列间距,窄屏自动降为单列",
|
||||
"floatButton(props):悬浮操作按钮,固定停靠左下 / 右下角,label 缺省为纯图标圆形,badge 走角标,tooltip 兼作 aria-label",
|
||||
"mentions(props):@提及输入,textarea 内键入 @ 弹候选(名字 / 关键词过滤),↑↓ 环形移动、Enter/Tab 选中、Esc 只收本层,选中按光标替换并写回 value",
|
||||
"treeSelect(props):树形选择,按钮触发弹层并复用 tree(搜索 / 多选 / 级联),单选选中即收起、多选保持展开,Esc 与点外收起"
|
||||
"treeSelect(props):树形选择,按钮触发弹层并复用 tree(搜索 / 多选 / 级联),单选选中即收起、多选保持展开,Esc 与点外收起",
|
||||
"link(props):链接,站内跳转与站外跳转统一入口,external 自动补 ↗ 与 rel=noopener noreferrer 并给读屏播报"
|
||||
],
|
||||
"keyTypes": [
|
||||
"ButtonProps",
|
||||
@@ -3260,6 +3261,62 @@
|
||||
"选中值只存 key(单选 value / 多选 checked 数组),显示名由 nodes 反查——改 label 不需要迁移数据",
|
||||
"禁用单个节点用 nodes 里的 disabled(该节点不可选,子节点仍可选);禁用整个控件用 disabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "link",
|
||||
"title": "链接 Link",
|
||||
"demo": "sec-link",
|
||||
"props": [
|
||||
{
|
||||
"n": "href",
|
||||
"t": "string",
|
||||
"r": 1,
|
||||
"d": "目标地址:站内相对路径或绝对 URL"
|
||||
},
|
||||
{
|
||||
"n": "label",
|
||||
"t": "string",
|
||||
"r": 1,
|
||||
"d": "链接文案"
|
||||
},
|
||||
{
|
||||
"n": "target",
|
||||
"t": "'_self' | '_blank' | '_parent' | '_top'",
|
||||
"d": "打开方式;缺省同窗口。传 _blank 会自动按站外处理(补 rel 与图标)"
|
||||
},
|
||||
{
|
||||
"n": "external",
|
||||
"t": "boolean",
|
||||
"df": "false",
|
||||
"d": "站外链接:补 ↗ 图标、强制新窗口、加 rel=noopener noreferrer,并给读屏播报「在新窗口打开」"
|
||||
},
|
||||
{
|
||||
"n": "muted",
|
||||
"t": "boolean",
|
||||
"df": "false",
|
||||
"d": "弱化色,用于次要链接与页脚"
|
||||
},
|
||||
{
|
||||
"n": "disabled",
|
||||
"t": "boolean",
|
||||
"df": "false",
|
||||
"d": "禁用:去掉 href、拦点击,保留可聚焦与 aria-disabled(仍能被 Tab 到,读屏能听出不可用)"
|
||||
},
|
||||
{
|
||||
"n": "onClick",
|
||||
"t": "(e: Event) => void",
|
||||
"k": 1,
|
||||
"d": "点击回调(禁用时不触发;可用于埋点,returnValue 不影响默认跳转)"
|
||||
}
|
||||
],
|
||||
"example": "link({ href: '/docs', label: '查看文档' }); link({ href: 'https://example.com', label: '外部站点', external: true }); link({ href: '#', label: '说明页', muted: true })",
|
||||
"steps": [
|
||||
"站内跳转用相对路径、站外用 external: true:会自动补 ↗、target=_blank、rel=noopener noreferrer,并给读屏播报开了新窗口",
|
||||
"页脚 / 次要说明用 muted 弱化;正文里的行内链接保持默认主色,与 typography 的 underline 区分用途(后者不跳转)",
|
||||
"需要「点了先确认再跳」这类拦截,在 onClick 里做判断后自行处理,组件不接管默认行为(除禁用外都放行)",
|
||||
"禁用态只是去掉 href 并保持 aria-disabled,不做视觉上的「像普通文字」处理——需要完全不可聚焦请在外层控制渲染",
|
||||
"纯样式的「看起来像链接」用 typography({ underline: true }),不要为了样式引入带 href 的 a 标签"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4308,3 +4308,55 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); }
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* —— 链接:颜色走令牌,悬停下划线渐进显现 —— */
|
||||
.ua-link {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 2px;
|
||||
color: var(--ua-color-accent);
|
||||
text-decoration: none;
|
||||
text-underline-offset: 2px;
|
||||
text-decoration-thickness: 1px;
|
||||
transition: color 0.15s ease, text-decoration-color 0.15s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ua-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.ua-link:focus-visible {
|
||||
outline: 2px solid var(--ua-color-accent);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--ua-radius-sm);
|
||||
}
|
||||
.ua-link.is-muted {
|
||||
color: var(--ua-color-text-muted);
|
||||
}
|
||||
.ua-link.is-muted:hover {
|
||||
color: var(--ua-color-text);
|
||||
}
|
||||
.ua-link.is-disabled {
|
||||
color: var(--ua-color-text-muted);
|
||||
cursor: not-allowed;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ua-link-icon {
|
||||
font-size: 0.85em;
|
||||
line-height: 1;
|
||||
}
|
||||
.ua-visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0); /* 只给读屏,视觉不可见 */
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ua-link {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user