one-design-next 0.0.29 → 0.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -115,6 +115,8 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
115
115
  _useState8 = _slicedToArray(_useState7, 2),
116
116
  triggerActiveIndex = _useState8[0],
117
117
  setTriggerActiveIndex = _useState8[1];
118
+ /** trigger menu 的命令式句柄:导航语义在 SkillSlot 内,composer 只转发 ↑↓。 */
119
+ var triggerSkillSlotRef = useRef(null);
118
120
  /** lite 模式下由单行升格到多行布局;粘性:只在 text 清空时复位 */
119
121
  var _useState9 = useState(false),
120
122
  _useState10 = _slicedToArray(_useState9, 2),
@@ -481,11 +483,16 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
481
483
  * `@` 触发本轮不接(mention 数据源待业务接入),onTriggerChange 拿到也不弹。 */
482
484
  var triggerMenuVisible = !!(triggerInfo && triggerInfo.trigger === '/' && skills && skills.length > 0);
483
485
 
484
- /* query 变化或浮层关闭 → 高亮项重置到第一项。
486
+ /* query 变化或浮层关闭 → 高亮项重置到「最上方的可用项」(跳过 disabled)。
485
487
  * 用 query 而不是 triggerInfo 整体当依赖:rect 变化(光标移位)不应该 reset。 */
486
488
  useEffect(function () {
487
- setTriggerActiveIndex(0);
488
- }, [triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.query, triggerInfo == null]);
489
+ var _ref3;
490
+ var list = (_ref3 = filteredSkills !== null && filteredSkills !== void 0 ? filteredSkills : skills) !== null && _ref3 !== void 0 ? _ref3 : [];
491
+ var first = list.findIndex(function (s) {
492
+ return !s.disabled;
493
+ });
494
+ setTriggerActiveIndex(first === -1 ? 0 : first);
495
+ }, [filteredSkills, skills, triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.query, triggerInfo == null]);
489
496
 
490
497
  /* editor 在 trigger 激活时把 ↑↓Enter Esc Tab 丢过来。返回 true = editor 别管了。
491
498
  * - ↑↓ 改高亮 index(夹在 [0, max] 内)
@@ -493,25 +500,25 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
493
500
  * - Escape 关 menu,焦点保留在 editor */
494
501
  var handleTriggerKeyDown = useCallback(function (e) {
495
502
  if (!triggerMenuVisible) return false;
496
- var list = filteredSkills;
497
- var max = list ? list.length - 1 : -1;
503
+
504
+ /* ↑↓ 导航完全委托给 SkillSlot(唯一事实源:跳过 disabled、夹边、滚动进视口),
505
+ * composer 焦点留在 editor,仅把按键转发进去。 */
498
506
  if (e.key === 'ArrowDown') {
507
+ var _triggerSkillSlotRef$;
499
508
  e.preventDefault();
500
- if (max >= 0) setTriggerActiveIndex(function (i) {
501
- return Math.min(i + 1, max);
502
- });
509
+ (_triggerSkillSlotRef$ = triggerSkillSlotRef.current) === null || _triggerSkillSlotRef$ === void 0 || _triggerSkillSlotRef$.moveActive('down');
503
510
  return true;
504
511
  }
505
512
  if (e.key === 'ArrowUp') {
513
+ var _triggerSkillSlotRef$2;
506
514
  e.preventDefault();
507
- if (max >= 0) setTriggerActiveIndex(function (i) {
508
- return Math.max(i - 1, 0);
509
- });
515
+ (_triggerSkillSlotRef$2 = triggerSkillSlotRef.current) === null || _triggerSkillSlotRef$2 === void 0 || _triggerSkillSlotRef$2.moveActive('up');
510
516
  return true;
511
517
  }
512
518
  if (e.key === 'Enter' && !e.shiftKey || e.key === 'Tab') {
519
+ var _triggerSkillSlotRef$3, _triggerSkillSlotRef$4;
513
520
  e.preventDefault();
514
- var _skill = list === null || list === void 0 ? void 0 : list[triggerActiveIndex];
521
+ var _skill = (_triggerSkillSlotRef$3 = (_triggerSkillSlotRef$4 = triggerSkillSlotRef.current) === null || _triggerSkillSlotRef$4 === void 0 ? void 0 : _triggerSkillSlotRef$4.getActiveSkill()) !== null && _triggerSkillSlotRef$3 !== void 0 ? _triggerSkillSlotRef$3 : filteredSkills === null || filteredSkills === void 0 ? void 0 : filteredSkills[triggerActiveIndex];
515
522
  if (_skill) handleSelectSkill(_skill, 'trigger');
516
523
  return true;
517
524
  }
@@ -532,7 +539,9 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
532
539
  var el = triggerAnchorRef.current;
533
540
  if (!el || !triggerInfo) return;
534
541
  el.style.left = "".concat(triggerInfo.rect.left, "px");
535
- el.style.top = "".concat(triggerInfo.rect.bottom, "px");
542
+ /* 锚到 caret 行顶部,配合 placement=topLeft menu 浮在输入内容上方,
543
+ * 不遮挡正在输入的文字(空间不足时 Popover flip 会自动回落到下方)。 */
544
+ el.style.top = "".concat(triggerInfo.rect.top, "px");
536
545
  }, [triggerInfo]);
537
546
 
538
547
  /* ---- Derived ---- */
@@ -825,7 +834,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
825
834
  var triggerMenu = skills && skills.length > 0 ? /*#__PURE__*/React.createElement(Popover, {
826
835
  trigger: "click",
827
836
  arrowed: false,
828
- placement: "bottomLeft",
837
+ placement: "topLeft",
829
838
  offset: 4,
830
839
  visible: triggerMenuVisible,
831
840
  onVisibleChange: function onVisibleChange(v) {
@@ -837,6 +846,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
837
846
  },
838
847
  popupClassName: "odn-composer-skill-popup",
839
848
  popup: /*#__PURE__*/React.createElement(SkillSlot, {
849
+ ref: triggerSkillSlotRef,
840
850
  variant: "menu",
841
851
  skills: filteredSkills !== null && filteredSkills !== void 0 ? filteredSkills : [],
842
852
  activeIndex: triggerActiveIndex,
package/dist/index.d.ts CHANGED
@@ -64,7 +64,7 @@ export { default as Invocation, InvocationParamPopover, type InvocationProps, ty
64
64
  export { type ComposerParamPanelContext } from './composer/param-panel';
65
65
  export { default as Mention, type MentionProps } from './mention';
66
66
  export { default as ChatItem, type ChatItemProps } from './chat-item';
67
- export { default as SkillSlot, type SkillSlotProps } from './skill-slot';
67
+ export { default as SkillSlot, type SkillSlotHandle, type SkillSlotProps } from './skill-slot';
68
68
  export { default as ErrorBlock, type ErrorBlockProps } from './error-block';
69
69
  export { default as Fab, type FabProps } from './fab';
70
70
  export { default as MessageImage, type MessageImageProps, type MessageImageItem } from './message-image';
@@ -8,22 +8,28 @@ export interface SkillSlotProps {
8
8
  value?: string | null;
9
9
  onSelect?: (skill: SkillItem) => void;
10
10
  /**
11
- * 受控的"键盘高亮"项 index(区别于 `value` 的"业务选中"语义)。
12
- * 仅 menu 变体响应。鼠标 hover 与键盘高亮共享同一套视觉(HoverFill),
13
- * 鼠标交互时优先级高于键盘;鼠标离开后键盘 activeIndex 才接管视觉。
11
+ * 受控的"键盘高亮"项 index(区别于 `value` 的"业务选中"语义)。仅 menu 变体响应。
14
12
  *
15
- * - 不传 不接管键盘高亮,纯鼠标交互
16
- * - 传入 → 鼠标 hover item 会回调 `onActiveIndexChange` 同步外部状态;
17
- * 外部按 ↑↓ activeIndex 时,SkillSlot 通过给目标 HoverFill dispatch
18
- * 合成 `mouseover` 让其触发原生视觉链路(与真实鼠标 hover 完全等价)
13
+ * 键盘导航语义(↑↓/Home/End 移动、跳过 disabled、夹边、滚动进视口)由 SkillSlot
14
+ * 自己负责,是唯一事实源。两种驱动方式:
15
+ * - 不传 `activeIndex` 非受控:menu 容器自身可聚焦,直接响应键盘。
16
+ * - 传入 `activeIndex` 受控:业务持有高亮 state(如 composer 焦点留在 editor,
17
+ * 通过 `ref` 调用 `moveActive` 转发 ↑↓),SkillSlot 通过 `onActiveIndexChange`
18
+ * 回写。鼠标 hover 也走同一套 index,保证鼠标/键盘指向同一项。
19
19
  */
20
20
  activeIndex?: number;
21
21
  onActiveIndexChange?: (index: number) => void;
22
22
  className?: string;
23
23
  style?: React.CSSProperties;
24
24
  }
25
- export declare function SkillSlot({ skills, variant, value, onSelect, activeIndex, onActiveIndexChange, className, style, }: SkillSlotProps): import("react").JSX.Element | null;
26
- export declare namespace SkillSlot {
27
- var displayName: string;
25
+ /** 命令式句柄:供"焦点不在 menu 上"的宿主(如 composer contenteditable)转发键盘。 */
26
+ export interface SkillSlotHandle {
27
+ /** 按方向移动高亮项,自动跳过 disabled、夹边。 */
28
+ moveActive: (dir: 'up' | 'down' | 'home' | 'end') => void;
29
+ /** 当前高亮项(无则 null)。 */
30
+ getActiveSkill: () => SkillItem | null;
31
+ /** 当前高亮 index(无则 -1)。 */
32
+ getActiveIndex: () => number;
28
33
  }
34
+ export declare const SkillSlot: import("react").ForwardRefExoticComponent<SkillSlotProps & import("react").RefAttributes<SkillSlotHandle>>;
29
35
  export default SkillSlot;
@@ -1,8 +1,22 @@
1
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
2
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
5
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
+ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
1
8
  import HoverFill from "../hover-fill";
2
9
  import Icon from "../icon";
3
10
  import Tooltip from "../tooltip";
4
11
  import "./style";
5
- export function SkillSlot(_ref) {
12
+
13
+ /* 给 listbox / option 生成稳定且唯一的 id 前缀(aria-activedescendant 需要)。
14
+ * 模块级自增 + useState 初始化,保证每个实例 mount 期间 id 稳定。 */
15
+ var skillSlotIdSeq = 0;
16
+
17
+ /** 命令式句柄:供"焦点不在 menu 上"的宿主(如 composer 的 contenteditable)转发键盘。 */
18
+
19
+ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref, ref) {
6
20
  var skills = _ref.skills,
7
21
  _ref$variant = _ref.variant,
8
22
  variant = _ref$variant === void 0 ? 'bar' : _ref$variant,
@@ -20,18 +34,151 @@ export function SkillSlot(_ref) {
20
34
  * activeIndex → highlighted 转移过来,鼠标走后 highlighted 接力。
21
35
  *
22
36
  * 颜色用 HoverFill 默认 hoverColor 同值(rgba(33,34,38,0.05)),过渡也用
23
- * 颜色 transition,视觉看起来一致。 */
37
+ * 颜色 transition,视觉看起来一致。
38
+ *
39
+ * 键盘导航(仅 menu):
40
+ * - 受控:业务传 `activeIndex`(如 composer 把 editor 的 ↑↓ 转发进来),
41
+ * 组件只反映高亮 + 回调 `onActiveIndexChange`,自身不维护状态。
42
+ * - 非受控:未传 `activeIndex` 时,menu 容器自身可聚焦(tabIndex),内部
43
+ * 维护高亮 index,监听 ↑↓ / Home / End 移动、Enter / Space 选中,
44
+ * 并自动跳过 disabled 项、把高亮项滚动进视口。 */
45
+
46
+ var _useState = useState(function () {
47
+ return "odn-skill-slot-".concat(++skillSlotIdSeq);
48
+ }),
49
+ _useState2 = _slicedToArray(_useState, 1),
50
+ listboxId = _useState2[0];
51
+ var _useState3 = useState(-1),
52
+ _useState4 = _slicedToArray(_useState3, 2),
53
+ internalActiveIndex = _useState4[0],
54
+ setInternalActiveIndex = _useState4[1];
55
+ var isControlled = activeIndex != null;
56
+ var currentIndex = isControlled ? activeIndex : internalActiveIndex;
57
+ var itemRefs = useRef([]);
58
+
59
+ /* disabled 与 active 互斥:已选中项不算 disabled(与渲染逻辑保持一致)。 */
60
+ var isItemDisabled = function isItemDisabled(skill) {
61
+ return !!skill.disabled && value !== skill.id;
62
+ };
63
+
64
+ /* 高亮项变化时滚动进视口(menu 列表可能超出浮层高度)。 */
65
+ useEffect(function () {
66
+ var _itemRefs$current$cur;
67
+ if (variant !== 'menu' || currentIndex < 0) return;
68
+ (_itemRefs$current$cur = itemRefs.current[currentIndex]) === null || _itemRefs$current$cur === void 0 || _itemRefs$current$cur.scrollIntoView({
69
+ block: 'nearest'
70
+ });
71
+ }, [variant, currentIndex]);
72
+ var applyActiveIndex = function applyActiveIndex(next) {
73
+ if (isControlled) onActiveIndexChange === null || onActiveIndexChange === void 0 || onActiveIndexChange(next);else setInternalActiveIndex(next);
74
+ };
75
+
76
+ /* 从 start 起按 dir 方向找第一个非 disabled 项;找不到返回 -1(不环绕,夹边)。 */
77
+ var findEnabled = function findEnabled(start, dir) {
78
+ for (var i = start; i >= 0 && i < skills.length; i += dir) {
79
+ if (!isItemDisabled(skills[i])) return i;
80
+ }
81
+ return -1;
82
+ };
83
+
84
+ /* 唯一的导航语义入口:按方向算出下一个可用项并应用。键盘事件与命令式 ref 共用。 */
85
+ var step = function step(dir) {
86
+ if (skills.length === 0) return;
87
+ var next = -1;
88
+ switch (dir) {
89
+ case 'down':
90
+ next = findEnabled(currentIndex < 0 ? 0 : currentIndex + 1, 1);
91
+ break;
92
+ case 'up':
93
+ next = findEnabled(currentIndex < 0 ? skills.length - 1 : currentIndex - 1, -1);
94
+ break;
95
+ case 'home':
96
+ next = findEnabled(0, 1);
97
+ break;
98
+ case 'end':
99
+ next = findEnabled(skills.length - 1, -1);
100
+ break;
101
+ }
102
+ if (next !== -1) applyActiveIndex(next);
103
+ };
104
+
105
+ /* 命令式句柄:让焦点不在 menu 上的宿主(composer)把 ↑↓ 转发进来,
106
+ * 导航/选中语义仍只有 SkillSlot 这一份。 */
107
+ useImperativeHandle(ref, function () {
108
+ return {
109
+ moveActive: step,
110
+ getActiveSkill: function getActiveSkill() {
111
+ return currentIndex >= 0 && currentIndex < skills.length ? skills[currentIndex] : null;
112
+ },
113
+ getActiveIndex: function getActiveIndex() {
114
+ return currentIndex;
115
+ }
116
+ };
117
+ },
118
+ // currentIndex / skills 变化时句柄需读到最新值
119
+ [currentIndex, skills]);
120
+ var handleMenuKeyDown = function handleMenuKeyDown(e) {
121
+ if (skills.length === 0) return;
122
+ switch (e.key) {
123
+ case 'ArrowDown':
124
+ e.preventDefault();
125
+ step('down');
126
+ break;
127
+ case 'ArrowUp':
128
+ e.preventDefault();
129
+ step('up');
130
+ break;
131
+ case 'Home':
132
+ e.preventDefault();
133
+ step('home');
134
+ break;
135
+ case 'End':
136
+ e.preventDefault();
137
+ step('end');
138
+ break;
139
+ case 'Enter':
140
+ case ' ':
141
+ {
142
+ if (currentIndex >= 0 && currentIndex < skills.length && !isItemDisabled(skills[currentIndex])) {
143
+ e.preventDefault();
144
+ onSelect === null || onSelect === void 0 || onSelect(skills[currentIndex]);
145
+ }
146
+ break;
147
+ }
148
+ default:
149
+ break;
150
+ }
151
+ };
152
+
153
+ /* 非受控时获得焦点 → 默认高亮第一个可用项。 */
154
+ var handleMenuFocus = function handleMenuFocus() {
155
+ if (isControlled || internalActiveIndex >= 0) return;
156
+ var next = findEnabled(0, 1);
157
+ if (next !== -1) setInternalActiveIndex(next);
158
+ };
24
159
 
160
+ /* 非受控时焦点离开整个 menu → 清掉高亮,避免失焦后残留。 */
161
+ var handleMenuBlur = function handleMenuBlur(e) {
162
+ if (isControlled) return;
163
+ if (e.currentTarget.contains(e.relatedTarget)) return;
164
+ setInternalActiveIndex(-1);
165
+ };
25
166
  if (skills.length === 0) return null;
26
167
  return /*#__PURE__*/React.createElement("div", {
27
168
  "data-odn-skill-slot": true,
28
169
  "data-odn-skill-slot-variant": variant,
29
- role: variant === 'menu' && activeIndex != null ? 'listbox' : undefined,
170
+ id: variant === 'menu' ? listboxId : undefined,
171
+ role: variant === 'menu' ? 'listbox' : undefined,
172
+ tabIndex: variant === 'menu' ? 0 : undefined,
173
+ "aria-activedescendant": variant === 'menu' && currentIndex >= 0 ? "".concat(listboxId, "-opt-").concat(currentIndex) : undefined,
174
+ onKeyDown: variant === 'menu' ? handleMenuKeyDown : undefined,
175
+ onFocus: variant === 'menu' ? handleMenuFocus : undefined,
176
+ onBlur: variant === 'menu' ? handleMenuBlur : undefined,
30
177
  className: className,
31
178
  style: style
32
179
  }, skills.map(function (skill, index) {
33
180
  var active = value === skill.id;
34
- var highlighted = activeIndex === index;
181
+ var highlighted = currentIndex === index;
35
182
  /* disabled 与 active 互斥时,active 优先(已选中不应再渲染禁用样态)。
36
183
  * onSelect 通过 button[disabled] 自然短路;HoverFill 也走 disabled 关掉 hover 反馈。 */
37
184
  var disabled = !!skill.disabled && !active;
@@ -61,20 +208,23 @@ export function SkillSlot(_ref) {
61
208
  }, skill.description));
62
209
  }
63
210
  if (variant === 'menu') {
64
- /* HoverFill 包 button,视觉由 HoverFill 命令式控制。
65
- * 键盘 ↑↓ 走外部 effect itemHostRefs[i] dispatch mouseover,
66
- * React 转成 onMouseEnter,HoverFill 自然走原生 hover 视觉链路。
67
- * 视觉只有 HoverFill 一套,鼠标键盘视觉本质完全相同。 */
211
+ /* HoverFill 包 button:鼠标 hover 视觉由 HoverFill 命令式控制;
212
+ * 键盘高亮走 button 上的 data-odn-skill-slot-item-highlighted ::before 层。
213
+ * 两层互斥(hover 时不画 ::before),颜色同值,视觉一致。 */
68
214
  return /*#__PURE__*/React.createElement(HoverFill, {
69
215
  key: skill.id,
70
216
  "data-odn-skill-slot-item-wrap": true,
71
217
  bgClassName: "odn-skill-slot-item-bg",
72
218
  disabled: active || disabled,
73
219
  onMouseEnter: function onMouseEnter() {
74
- /* 鼠标进入:把键盘 activeIndex 同步过来,让两条轨道始终指向同一项,
75
- * 避免鼠标移开后键盘 active 还指向另一项造成"鬼影高亮"。 */
76
- if (!disabled && onActiveIndexChange && activeIndex !== index) {
77
- onActiveIndexChange(index);
220
+ /* 鼠标进入:把高亮 index 同步过来,让鼠标/键盘两条轨道始终指向同一项,
221
+ * 避免鼠标移开后键盘 active 还指向另一项造成"鬼影高亮"。
222
+ * 受控走 onActiveIndexChange,非受控更新内部 state。 */
223
+ if (disabled) return;
224
+ if (isControlled) {
225
+ if (onActiveIndexChange && activeIndex !== index) onActiveIndexChange(index);
226
+ } else if (internalActiveIndex !== index) {
227
+ setInternalActiveIndex(index);
78
228
  }
79
229
  }
80
230
  /* 阻止 contenteditable 在 mousedown 时 blur,否则点 menu item 的瞬间
@@ -84,12 +234,17 @@ export function SkillSlot(_ref) {
84
234
  }
85
235
  }, /*#__PURE__*/React.createElement("button", {
86
236
  type: "button",
237
+ id: "".concat(listboxId, "-opt-").concat(index),
238
+ ref: function ref(el) {
239
+ itemRefs.current[index] = el;
240
+ },
87
241
  "data-odn-skill-slot-item": true,
88
242
  "data-odn-skill-slot-item-active": active || undefined,
89
243
  "data-odn-skill-slot-item-highlighted": highlighted || undefined,
90
244
  "data-odn-skill-slot-item-disabled": disabled || undefined,
91
- role: activeIndex != null ? 'option' : undefined,
92
- "aria-selected": activeIndex != null ? highlighted : undefined,
245
+ role: "option",
246
+ "aria-selected": highlighted,
247
+ tabIndex: -1,
93
248
  disabled: disabled,
94
249
  "aria-disabled": disabled || undefined,
95
250
  onClick: function onClick() {
@@ -134,6 +289,6 @@ export function SkillSlot(_ref) {
134
289
  }
135
290
  return barButton;
136
291
  }));
137
- }
292
+ });
138
293
  SkillSlot.displayName = 'SkillSlot';
139
294
  export default SkillSlot;
@@ -16,7 +16,7 @@
16
16
  }
17
17
 
18
18
  .odn-skill-slot-item-bg {
19
- border-radius: var(--odn-border-radius);
19
+ border-radius: 0;
20
20
  }
21
21
 
22
22
  /* ---- variant: menu ---- */
@@ -24,6 +24,11 @@
24
24
  display: flex;
25
25
  flex-direction: column;
26
26
  padding: 4px 0;
27
+ /* 容器走 aria-activedescendant 模式(焦点留在 listbox,高亮项由 ::before 指示),
28
+ * 故去掉容器自身的焦点描边,避免与高亮视觉重复。 */
29
+ }
30
+ [data-odn-skill-slot][data-odn-skill-slot-variant=menu]:focus, [data-odn-skill-slot][data-odn-skill-slot-variant=menu]:focus-visible {
31
+ outline: none;
27
32
  }
28
33
 
29
34
  [data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-item-wrap] {
@@ -44,7 +49,8 @@
44
49
  }
45
50
  [data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-item] > [data-odn-icon] {
46
51
  flex-shrink: 0;
47
- margin-top: 2px;
52
+ /* 与标签首行(line-height 22px)的视觉中线对齐:(22 - 14) / 2 = 4px */
53
+ margin-top: 4px;
48
54
  }
49
55
  [data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-item] {
50
56
  /* 键盘高亮:独立视觉层,颜色与 HoverFill 默认 hoverColor 完全一致,过渡用
@@ -63,7 +69,7 @@
63
69
  position: absolute;
64
70
  inset: 0;
65
71
  background: rgba(33, 34, 38, 0.05);
66
- border-radius: var(--odn-border-radius);
72
+ border-radius: 0;
67
73
  transition: background-color 0.18s ease;
68
74
  pointer-events: none;
69
75
  z-index: -1;
@@ -192,7 +198,7 @@
192
198
  [data-odn-skill-slot-item-content] {
193
199
  display: flex;
194
200
  flex-direction: column;
195
- gap: 4px;
201
+ gap: 2px;
196
202
  min-width: 0;
197
203
  flex: 1;
198
204
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one-design-next",
3
- "version": "0.0.29",
3
+ "version": "0.0.30",
4
4
  "description": "One Design Next from TAD@tencent.com",
5
5
  "packageManager": "pnpm@10.33.0",
6
6
  "module": "dist/index.js",