one-design-next 0.0.115 → 0.0.116
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.
- package/dist/composer/editor.js +92 -15
- package/dist/composer/style/index.css +68 -0
- package/dist/composer/utils.d.ts +9 -0
- package/dist/composer/utils.js +12 -2
- package/dist/skill-slot/index.js +30 -18
- package/dist/skill-slot/style/index.css +55 -15
- package/package.json +1 -1
package/dist/composer/editor.js
CHANGED
|
@@ -29,7 +29,7 @@ import { useChipManager } from "./hooks/useChipManager";
|
|
|
29
29
|
import { useChipSelectionMarker } from "./hooks/useChipSelectionMarker";
|
|
30
30
|
import { chipHasEditableParams, ComposerInlineRef } from "./inline-ref";
|
|
31
31
|
import { resolveHintTarget } from "./segments";
|
|
32
|
-
import { COMPOSER_HINT_ATTR, createLineRoot, createLineRootWithText, ensureCaretAnchorAfter, ensureLineRoots, getPlainText, isEditorEmpty, isLineRoot, LINE_ATTR_NAME, normalizeEditorDom, probeTrigger, stripInvisibleChars, ZWSP } from "./utils";
|
|
32
|
+
import { COMPOSER_HINT_ACCEPT_ATTR, COMPOSER_HINT_ACCEPT_SELECTOR, COMPOSER_HINT_ATTR, createLineRoot, createLineRootWithText, ensureCaretAnchorAfter, ensureLineRoots, getPlainText, isEditorEmpty, isLineRoot, LINE_ATTR_NAME, normalizeEditorDom, probeTrigger, stripInvisibleChars, ZWSP } from "./utils";
|
|
33
33
|
|
|
34
34
|
/** Composer editor 触发态对外信号——告诉父组件该弹浮层选 skill 了。 */
|
|
35
35
|
|
|
@@ -98,6 +98,34 @@ var findDirectHint = function findDirectHint(line) {
|
|
|
98
98
|
return null;
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* hint 末尾的「TAB 采纳」小块:点它 = 按 Tab 采纳这段模板文案。
|
|
103
|
+
*
|
|
104
|
+
* 与 hint 文本是同级节点(见 `COMPOSER_HINT_ACCEPT_ATTR` 的说明),由
|
|
105
|
+
* `syncHintNode` 一起插入 / 移除,不进 value / 剪贴板 / segments。
|
|
106
|
+
* 视觉在 `style/index.scss`(`[data-odn-composer-hint-accept]`)。
|
|
107
|
+
*/
|
|
108
|
+
var createHintAcceptNode = function createHintAcceptNode() {
|
|
109
|
+
var accept = document.createElement('span');
|
|
110
|
+
accept.setAttribute(COMPOSER_HINT_ACCEPT_ATTR, '');
|
|
111
|
+
accept.contentEditable = 'false';
|
|
112
|
+
/* 刻意不加 role="button" / aria-label / title:
|
|
113
|
+
* - 它不可聚焦、也没有 Enter/Space 激活逻辑,挂 button 语义等于给读屏用户
|
|
114
|
+
* 一个按不动的控件(无障碍反而变成障碍)。真正的键盘路径就是它写着的
|
|
115
|
+
* Tab 键本身,可见文案已经说清动作。
|
|
116
|
+
* - aria-label 会盖掉可见文案,反而违反「可见标签即无障碍名」。
|
|
117
|
+
* - title 是浏览器原生 tooltip:延迟出现、样式不可控、会盖住灰字。 */
|
|
118
|
+
|
|
119
|
+
var key = document.createElement('span');
|
|
120
|
+
key.setAttribute('data-odn-composer-hint-accept-key', '');
|
|
121
|
+
key.textContent = 'TAB';
|
|
122
|
+
var label = document.createElement('span');
|
|
123
|
+
label.setAttribute('data-odn-composer-hint-accept-label', '');
|
|
124
|
+
label.textContent = '采纳';
|
|
125
|
+
accept.append(key, label);
|
|
126
|
+
return accept;
|
|
127
|
+
};
|
|
128
|
+
|
|
101
129
|
/**
|
|
102
130
|
* insertContent 的插入点:最后一个行容器的内容末尾。
|
|
103
131
|
*
|
|
@@ -359,12 +387,18 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
359
387
|
* 就不存在了,caret 只能落在整个 host 之后(= 提示文本末尾)。
|
|
360
388
|
*
|
|
361
389
|
* 它不归 React 管(editor 的 children 由 contenteditable 自治),由 fireChange
|
|
362
|
-
* 按结构谓词增删/挪位;`getPlainText` 会整棵子树跳过,所以不进 value / 剪贴板。
|
|
390
|
+
* 按结构谓词增删/挪位;`getPlainText` 会整棵子树跳过,所以不进 value / 剪贴板。
|
|
391
|
+
*
|
|
392
|
+
* 末尾的「TAB 采纳」小块是 hint 文本的**兄弟节点**(不是子节点):hint 的
|
|
393
|
+
* textContent 必须保持纯提示文案(采纳时直接落成正文,调试面板也按它读)。 */
|
|
363
394
|
var hintNodeRef = useRef(null);
|
|
395
|
+
var hintAcceptNodeRef = useRef(null);
|
|
364
396
|
var removeHintNode = useCallback(function () {
|
|
365
|
-
var _hintNodeRef$current;
|
|
397
|
+
var _hintNodeRef$current, _hintAcceptNodeRef$cu;
|
|
366
398
|
(_hintNodeRef$current = hintNodeRef.current) === null || _hintNodeRef$current === void 0 || _hintNodeRef$current.remove();
|
|
367
399
|
hintNodeRef.current = null;
|
|
400
|
+
(_hintAcceptNodeRef$cu = hintAcceptNodeRef.current) === null || _hintAcceptNodeRef$cu === void 0 || _hintAcceptNodeRef$cu.remove();
|
|
401
|
+
hintAcceptNodeRef.current = null;
|
|
368
402
|
}, []);
|
|
369
403
|
var syncHintNode = useCallback(function (target) {
|
|
370
404
|
var _Array$from$find, _blankTail;
|
|
@@ -384,6 +418,11 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
384
418
|
hintNodeRef.current = node;
|
|
385
419
|
}
|
|
386
420
|
if (node.textContent !== target.text) node.textContent = target.text;
|
|
421
|
+
var accept = hintAcceptNodeRef.current;
|
|
422
|
+
if (!accept || !accept.isConnected) {
|
|
423
|
+
accept = createHintAcceptNode();
|
|
424
|
+
hintAcceptNodeRef.current = accept;
|
|
425
|
+
}
|
|
387
426
|
|
|
388
427
|
/* 落位:命中 chip 之后、**紧随其后的不可见锚点文本(ZWSP)之后**。
|
|
389
428
|
*
|
|
@@ -398,8 +437,9 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
398
437
|
var cur = host.nextSibling;
|
|
399
438
|
while (cur) {
|
|
400
439
|
var _cur$textContent;
|
|
401
|
-
// 跳过 hint
|
|
402
|
-
|
|
440
|
+
// 跳过 hint 自身与它的「TAB 采纳」小块:上一轮若排到了锚点前面,
|
|
441
|
+
// 这里顺手挪回末尾
|
|
442
|
+
if (cur === node || cur === accept) {
|
|
403
443
|
cur = cur.nextSibling;
|
|
404
444
|
continue;
|
|
405
445
|
}
|
|
@@ -426,6 +466,12 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
426
466
|
if (node.parentNode !== parent || node.previousSibling !== anchor) {
|
|
427
467
|
parent.insertBefore(node, anchor.nextSibling);
|
|
428
468
|
}
|
|
469
|
+
/* 「TAB 采纳」小块紧跟 hint 文案之后(同一套行内流),顺序必须是
|
|
470
|
+
* hint → 小块:caret 的合法落点只到 hint 之前,块本身
|
|
471
|
+
* contentEditable=false,不会成为落点。 */
|
|
472
|
+
if (accept.parentNode !== parent || accept.previousSibling !== node) {
|
|
473
|
+
parent.insertBefore(accept, node.nextSibling);
|
|
474
|
+
}
|
|
429
475
|
}, [removeHintNode]);
|
|
430
476
|
|
|
431
477
|
/** 把 caret 直接落到 hint 之前(= chip 右侧);没有 hint 时不动。 */
|
|
@@ -530,26 +576,45 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
530
576
|
recomputeTriggerRef.current = recomputeTrigger;
|
|
531
577
|
|
|
532
578
|
/* ─────────────────────────────────────
|
|
533
|
-
* Tab
|
|
579
|
+
* 采纳 hint(Tab / 点击「TAB 采纳」小块)
|
|
534
580
|
*
|
|
535
581
|
* hint 只是结构投影(不进 value);caret 恰好停在 chip 与 hint 之间
|
|
536
582
|
* (= 内容末尾)时按 Tab,表示用户接受这段模板,于是把提示文案落成正文:
|
|
537
583
|
* chip 之后出现内容 → 结构谓词不再命中 → 灰字自动移除,onChange 抛出的
|
|
538
584
|
* value 里带上这段文案,与用户自己敲进去的效果一致。
|
|
585
|
+
*
|
|
586
|
+
* 鼠标点末尾的「TAB 采纳」小块走**同一条链路**,只是带 `force`:
|
|
587
|
+
* 不要求点击前 caret 就停在 hint 之前,先把 caret 归位到 chip 右侧再落。
|
|
539
588
|
* ───────────────────────────────────── */
|
|
540
|
-
var acceptHintAsText = useCallback(function () {
|
|
589
|
+
var acceptHintAsText = useCallback(function (options) {
|
|
541
590
|
var _hint$textContent;
|
|
542
591
|
var el = editorRef.current;
|
|
543
592
|
var hint = hintNodeRef.current;
|
|
544
593
|
var sel = window.getSelection();
|
|
545
|
-
if (!el || !hint || !hint.isConnected || !sel
|
|
546
|
-
|
|
594
|
+
if (!el || !hint || !hint.isConnected || !sel) return false;
|
|
595
|
+
var range;
|
|
596
|
+
if (options !== null && options !== void 0 && options.force) {
|
|
597
|
+
/* 点击路径:mousedown 已被 preventDefault,焦点 / caret 都还没进来,
|
|
598
|
+
* 先聚焦再把 caret 落到 hint 之前(= 内容末尾),避免原生落点乱跳。 */
|
|
599
|
+
el.focus({
|
|
600
|
+
preventScroll: true
|
|
601
|
+
});
|
|
602
|
+
var target = caretBeforeHintIn(el);
|
|
603
|
+
if (!target) return false;
|
|
604
|
+
range = document.createRange();
|
|
605
|
+
range.setStart(target.node, target.offset);
|
|
606
|
+
range.collapse(true);
|
|
607
|
+
sel.removeAllRanges();
|
|
608
|
+
sel.addRange(range);
|
|
609
|
+
lastEditorRangeRef.current = range.cloneRange();
|
|
610
|
+
} else {
|
|
611
|
+
if (sel.rangeCount === 0) return false;
|
|
612
|
+
range = sel.getRangeAt(0);
|
|
613
|
+
if (!range.collapsed || !el.contains(range.startContainer)) return false;
|
|
614
|
+
/* 只认「chip 之后、hint 之前」这一个落点;caret 在别处时 Tab 仍归浏览器,
|
|
615
|
+
* 避免在任意位置误吞焦点切换。 */
|
|
616
|
+
if (!isCaretBeforeHint(range, el)) return false;
|
|
547
617
|
}
|
|
548
|
-
var range = sel.getRangeAt(0);
|
|
549
|
-
if (!range.collapsed || !el.contains(range.startContainer)) return false;
|
|
550
|
-
/* 只认「chip 之后、hint 之前」这一个落点;caret 在别处时 Tab 仍归浏览器,
|
|
551
|
-
* 避免在任意位置误吞焦点切换。 */
|
|
552
|
-
if (!isCaretBeforeHint(range, el)) return false;
|
|
553
618
|
var text = (_hint$textContent = hint.textContent) !== null && _hint$textContent !== void 0 ? _hint$textContent : '';
|
|
554
619
|
if (!text) return false;
|
|
555
620
|
|
|
@@ -566,6 +631,7 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
566
631
|
caret.collapse(true);
|
|
567
632
|
sel.removeAllRanges();
|
|
568
633
|
sel.addRange(caret);
|
|
634
|
+
lastEditorRangeRef.current = caret.cloneRange();
|
|
569
635
|
}
|
|
570
636
|
fireChange();
|
|
571
637
|
recomputeTrigger();
|
|
@@ -1061,6 +1127,17 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
1061
1127
|
if (e.button !== 0 || disabled) return;
|
|
1062
1128
|
// shift+click 是扩选,不劫持
|
|
1063
1129
|
if (e.shiftKey) return;
|
|
1130
|
+
|
|
1131
|
+
/* 「TAB 采纳」小块:点它等价于按 Tab(同一条采纳链路),且不要求点击前
|
|
1132
|
+
* caret 就停在 hint 之前。在 mousedown 阶段接管 + preventDefault,
|
|
1133
|
+
* 原生 caret 一帧都不会落到 hint 之后。 */
|
|
1134
|
+
if (e.target instanceof Element && e.target.closest(COMPOSER_HINT_ACCEPT_SELECTOR)) {
|
|
1135
|
+
e.preventDefault();
|
|
1136
|
+
acceptHintAsText({
|
|
1137
|
+
force: true
|
|
1138
|
+
});
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1064
1141
|
var el = editorRef.current;
|
|
1065
1142
|
var hint = hintNodeRef.current;
|
|
1066
1143
|
if (!el || !hint || !hint.isConnected) return;
|
|
@@ -1091,7 +1168,7 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
1091
1168
|
});
|
|
1092
1169
|
placeCaretBeforeHint();
|
|
1093
1170
|
requestScrollCaretIntoView();
|
|
1094
|
-
}, [disabled, handlePointerInput, placeCaretBeforeHint, requestScrollCaretIntoView]);
|
|
1171
|
+
}, [acceptHintAsText, disabled, handlePointerInput, placeCaretBeforeHint, requestScrollCaretIntoView]);
|
|
1095
1172
|
var writeClipboardPayload = useCallback(function (e, payload) {
|
|
1096
1173
|
if (!payload) return false;
|
|
1097
1174
|
e.clipboardData.setData(COMPOSER_CLIPBOARD_MIME, JSON.stringify(payload));
|
|
@@ -309,6 +309,74 @@
|
|
|
309
309
|
white-space: pre-wrap;
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
/* ------------------------------------------------------------------
|
|
313
|
+
* hint 末尾的「TAB 采纳」小块(点击 = 按 Tab 采纳,见 editor.tsx)
|
|
314
|
+
*
|
|
315
|
+
* 与 hint 文本是同级节点,一起增删。它不是正文:contentEditable=false、
|
|
316
|
+
* getPlainText 整棵跳过,点它走 force 版 acceptHintAsText。
|
|
317
|
+
* 尺寸对齐 Figma 9857:75425(62 × 22 / 圆角 6 / 内层键帽 28 × 16):
|
|
318
|
+
* - 编辑器固定 font-size 14 / line-height 24,块高 22 + top 1 → 视觉居中
|
|
319
|
+
* 且不撑高行盒(改编辑器行高时要同步调这个 1px)
|
|
320
|
+
* - 底色 black-2(5%)、键帽 bg-elevated + black-5(12%) 1px 描边
|
|
321
|
+
* ----------------------------------------------------------------- */
|
|
322
|
+
[data-odn-composer] [data-odn-composer-hint-accept] {
|
|
323
|
+
display: inline-flex;
|
|
324
|
+
align-items: center;
|
|
325
|
+
justify-content: center;
|
|
326
|
+
gap: 3px;
|
|
327
|
+
box-sizing: border-box;
|
|
328
|
+
height: 22px;
|
|
329
|
+
margin: 1px 0 0 4px;
|
|
330
|
+
padding: 0 4px 0 3px;
|
|
331
|
+
border-radius: 6px;
|
|
332
|
+
background: var(--odn-color-black-2);
|
|
333
|
+
color: var(--odn-color-solid-black-9);
|
|
334
|
+
font-size: 12px;
|
|
335
|
+
line-height: 20px;
|
|
336
|
+
white-space: nowrap;
|
|
337
|
+
vertical-align: top;
|
|
338
|
+
cursor: pointer;
|
|
339
|
+
user-select: none;
|
|
340
|
+
pointer-events: auto;
|
|
341
|
+
transition: background 0.15s ease-out, color 0.15s ease-out;
|
|
342
|
+
/* 底部 5% 太淡,hover 抬到 12%(约 2.4 倍)并加深文字,让「可点」更明确;
|
|
343
|
+
* active 再深一档。 */
|
|
344
|
+
}
|
|
345
|
+
[data-odn-composer] [data-odn-composer-hint-accept]:hover {
|
|
346
|
+
background: var(--odn-color-black-5);
|
|
347
|
+
color: var(--odn-color-solid-black-10);
|
|
348
|
+
}
|
|
349
|
+
[data-odn-composer] [data-odn-composer-hint-accept]:active {
|
|
350
|
+
background: var(--odn-color-black-6);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
[data-odn-composer] [data-odn-composer-hint-accept-key] {
|
|
354
|
+
display: inline-flex;
|
|
355
|
+
align-items: center;
|
|
356
|
+
justify-content: center;
|
|
357
|
+
box-sizing: border-box;
|
|
358
|
+
height: 16px;
|
|
359
|
+
padding: 0 4px;
|
|
360
|
+
border: 1px solid var(--odn-color-black-5);
|
|
361
|
+
border-radius: 4px;
|
|
362
|
+
/* 与 composer box 同为「浮在底层之上的面」,跟随主题变量而不是写死白色 */
|
|
363
|
+
background: var(--odn-color-bg-elevated);
|
|
364
|
+
font-size: 10px;
|
|
365
|
+
line-height: 10px;
|
|
366
|
+
letter-spacing: 0;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
[data-odn-composer] [data-odn-composer-hint-accept-label] {
|
|
370
|
+
font-size: 12px;
|
|
371
|
+
line-height: 20px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/* 禁用态:hint 仍按内容结构展示,但小块不可点(与 editor 冻结一致) */
|
|
375
|
+
[data-odn-composer] [data-odn-composer-editor][data-odn-composer-editor-disabled] [data-odn-composer-hint-accept] {
|
|
376
|
+
cursor: not-allowed;
|
|
377
|
+
pointer-events: none;
|
|
378
|
+
}
|
|
379
|
+
|
|
312
380
|
/* ------------------------------------------------------------------
|
|
313
381
|
* Block 行模型(caret-rewrite step 1/5)
|
|
314
382
|
* editor 直接子节点都是 [data-composer-line] div,每行渲染为独立 block。
|
package/dist/composer/utils.d.ts
CHANGED
|
@@ -22,6 +22,15 @@ export declare const LINE_ATTR_NAME = "data-composer-line";
|
|
|
22
22
|
*/
|
|
23
23
|
export declare const COMPOSER_HINT_ATTR = "data-odn-composer-skill-hint";
|
|
24
24
|
export declare const COMPOSER_HINT_SELECTOR = "[data-odn-composer-skill-hint]";
|
|
25
|
+
/**
|
|
26
|
+
* 派生 hint 末尾的「TAB 采纳」小块(可点击采纳)。
|
|
27
|
+
*
|
|
28
|
+
* 与 hint 文本是**兄弟节点**(不是子节点):hint 自身的 `textContent` 必须
|
|
29
|
+
* 保持纯提示文案——采纳时直接把这段文字落成正文,且 `getPlainText` /
|
|
30
|
+
* 调试面板都按 textContent 读它。小块同样不是正文,遍历时整棵跳过。
|
|
31
|
+
*/
|
|
32
|
+
export declare const COMPOSER_HINT_ACCEPT_ATTR = "data-odn-composer-hint-accept";
|
|
33
|
+
export declare const COMPOSER_HINT_ACCEPT_SELECTOR = "[data-odn-composer-hint-accept]";
|
|
25
34
|
/** 是否是行容器。 */
|
|
26
35
|
export declare function isLineRoot(node: Node | null | undefined): node is HTMLDivElement;
|
|
27
36
|
/** 创建一个新的空行容器。 */
|
package/dist/composer/utils.js
CHANGED
|
@@ -39,6 +39,16 @@ export var LINE_ATTR_NAME = 'data-composer-line';
|
|
|
39
39
|
export var COMPOSER_HINT_ATTR = 'data-odn-composer-skill-hint';
|
|
40
40
|
export var COMPOSER_HINT_SELECTOR = "[".concat(COMPOSER_HINT_ATTR, "]");
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* 派生 hint 末尾的「TAB 采纳」小块(可点击采纳)。
|
|
44
|
+
*
|
|
45
|
+
* 与 hint 文本是**兄弟节点**(不是子节点):hint 自身的 `textContent` 必须
|
|
46
|
+
* 保持纯提示文案——采纳时直接把这段文字落成正文,且 `getPlainText` /
|
|
47
|
+
* 调试面板都按 textContent 读它。小块同样不是正文,遍历时整棵跳过。
|
|
48
|
+
*/
|
|
49
|
+
export var COMPOSER_HINT_ACCEPT_ATTR = 'data-odn-composer-hint-accept';
|
|
50
|
+
export var COMPOSER_HINT_ACCEPT_SELECTOR = "[".concat(COMPOSER_HINT_ACCEPT_ATTR, "]");
|
|
51
|
+
|
|
42
52
|
/** 是否是行容器。 */
|
|
43
53
|
export function isLineRoot(node) {
|
|
44
54
|
var _hasAttribute, _ref;
|
|
@@ -292,8 +302,8 @@ export function getPlainText(editor) {
|
|
|
292
302
|
var out = '';
|
|
293
303
|
var visit = function visit(node) {
|
|
294
304
|
if (node instanceof HTMLElement) {
|
|
295
|
-
// 派生 hint
|
|
296
|
-
if (node.hasAttribute(COMPOSER_HINT_ATTR)) return;
|
|
305
|
+
// 派生 hint(含末尾的「TAB 采纳」小块)整棵子树都不是正文,直接跳过
|
|
306
|
+
if (node.hasAttribute(COMPOSER_HINT_ATTR) || node.hasAttribute(COMPOSER_HINT_ACCEPT_ATTR)) return;
|
|
297
307
|
var id = node.dataset[chipIdAttr];
|
|
298
308
|
if (id) {
|
|
299
309
|
out += createMarker(id);
|
package/dist/skill-slot/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import Icon from "../icon";
|
|
|
7
7
|
import Input from "../input";
|
|
8
8
|
import Popover from "../popover";
|
|
9
9
|
import { highlightQueryText } from "../stream-text/highlight-query-text";
|
|
10
|
+
import Tooltip from "../tooltip";
|
|
10
11
|
import "./style";
|
|
11
12
|
function skillMatchesQuery(skill, query) {
|
|
12
13
|
var _skill$description;
|
|
@@ -367,8 +368,15 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
367
368
|
};
|
|
368
369
|
var renderBarItem = function renderBarItem(skill) {
|
|
369
370
|
var disabled = !!skill.disabled;
|
|
370
|
-
|
|
371
|
+
|
|
372
|
+
/* bar 无行内描述:整条 bar 由外层 Tooltip.Group 共用一层浮层(暗色,见 style)。
|
|
373
|
+
* 这里只把「本项的描述」交给 Group,hover 绑定由 Group 自己挂到 button 上。 */
|
|
374
|
+
return /*#__PURE__*/React.createElement(Tooltip, {
|
|
371
375
|
key: skill.id,
|
|
376
|
+
popup: skill.description ? /*#__PURE__*/React.createElement("span", {
|
|
377
|
+
"data-odn-skill-slot-desc-full": true
|
|
378
|
+
}, skill.description) : null
|
|
379
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
372
380
|
type: "button",
|
|
373
381
|
"data-odn-skill-slot-item": true,
|
|
374
382
|
"data-odn-skill-slot-item-disabled": disabled || undefined,
|
|
@@ -379,21 +387,8 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
379
387
|
}
|
|
380
388
|
}, /*#__PURE__*/React.createElement(Icon, {
|
|
381
389
|
name: skill.icon,
|
|
382
|
-
size:
|
|
383
|
-
}), skill.label);
|
|
384
|
-
|
|
385
|
-
/* bar 无行内描述:整颗 chip hover 0.5s 后向上 Popover 出全文 */
|
|
386
|
-
if (skill.description) {
|
|
387
|
-
return /*#__PURE__*/React.createElement(Popover, {
|
|
388
|
-
key: skill.id,
|
|
389
|
-
placement: "top",
|
|
390
|
-
mouseEnterDelay: 0.5,
|
|
391
|
-
popup: /*#__PURE__*/React.createElement("span", {
|
|
392
|
-
"data-odn-skill-slot-desc-full": true
|
|
393
|
-
}, skill.description)
|
|
394
|
-
}, barButton);
|
|
395
|
-
}
|
|
396
|
-
return barButton;
|
|
390
|
+
size: 16
|
|
391
|
+
}), skill.label));
|
|
397
392
|
};
|
|
398
393
|
var listBody = skills.map(function (skill, index) {
|
|
399
394
|
if (variant === 'card') return renderCardItem(skill);
|
|
@@ -461,10 +456,27 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
461
456
|
} : {}), variant !== 'menu' && itemWidth !== undefined && itemWidth !== null ? {
|
|
462
457
|
'--odn-skill-slot-item-width': "".concat(itemWidth, "px")
|
|
463
458
|
} : {}), style);
|
|
464
|
-
|
|
459
|
+
|
|
460
|
+
/* bar:整条 bar 是一组 Tooltip(与 preview-panel 右上角按钮同款手感)——
|
|
461
|
+
* 共用一层浮层,已展开时在块之间横移是 0 延迟换文案 + 浮层跟着滑,
|
|
462
|
+
* 移出整组 200ms 后才收起,不会一块一块地闪断。
|
|
463
|
+
* 容器本身就是 Tooltip.Group 的 Tag,data-* / className / style 一并落上去。 */
|
|
464
|
+
if (variant === 'bar') {
|
|
465
|
+
return /*#__PURE__*/React.createElement(Tooltip.Group, {
|
|
466
|
+
as: "div",
|
|
467
|
+
"data-odn-skill-slot": true,
|
|
468
|
+
"data-odn-skill-slot-variant": "bar",
|
|
469
|
+
className: className,
|
|
470
|
+
style: rootStyle,
|
|
471
|
+
placement: "top",
|
|
472
|
+
mouseEnterDelay: 0.1,
|
|
473
|
+
popupClassName: "odn-skill-slot-bar-popup"
|
|
474
|
+
}, listBody);
|
|
475
|
+
}
|
|
476
|
+
if (variant === 'card') {
|
|
465
477
|
return /*#__PURE__*/React.createElement("div", {
|
|
466
478
|
"data-odn-skill-slot": true,
|
|
467
|
-
"data-odn-skill-slot-variant":
|
|
479
|
+
"data-odn-skill-slot-variant": "card",
|
|
468
480
|
className: className,
|
|
469
481
|
style: rootStyle
|
|
470
482
|
}, listBody);
|
|
@@ -237,39 +237,71 @@
|
|
|
237
237
|
margin: 0;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
/* ---- variant: bar ----
|
|
240
|
+
/* ---- variant: bar ----
|
|
241
|
+
* 白底胶囊 chip:999 圆角,14/22 文案 + 16 icon,块间距 12。
|
|
242
|
+
* 设计稿:默认态**无描边**(描边在稿里是隐藏的),hover 才浮出黑-3 细描边,
|
|
243
|
+
* 底色/文字不变——所以预置 1px 透明描边占位,避免 hover 时宽度跳 2px。 */
|
|
241
244
|
[data-odn-skill-slot][data-odn-skill-slot-variant=bar] {
|
|
242
245
|
display: flex;
|
|
243
246
|
flex-wrap: wrap;
|
|
244
|
-
gap:
|
|
247
|
+
gap: 12px;
|
|
245
248
|
}
|
|
246
249
|
|
|
247
250
|
[data-odn-skill-slot][data-odn-skill-slot-variant=bar] [data-odn-skill-slot-item] {
|
|
248
251
|
display: inline-flex;
|
|
249
252
|
align-items: center;
|
|
250
|
-
gap:
|
|
251
|
-
height:
|
|
253
|
+
gap: 4px;
|
|
254
|
+
height: 36px;
|
|
252
255
|
padding: 0 12px;
|
|
253
|
-
border-radius:
|
|
254
|
-
border: 1px solid
|
|
255
|
-
background: var(--odn-color-
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
border-radius: 999px;
|
|
257
|
+
border: 1px solid transparent;
|
|
258
|
+
background: var(--odn-color-white);
|
|
259
|
+
box-sizing: border-box;
|
|
260
|
+
/* 不参与压缩:容器放不下就整块换行。否则窄容器里 flex 会把块挤到
|
|
261
|
+
* 比文案还窄,14/22 的文案折成两行、溢出固定 36px 的胶囊。 */
|
|
262
|
+
flex: 0 0 auto;
|
|
263
|
+
white-space: nowrap;
|
|
264
|
+
font-size: 14px;
|
|
265
|
+
font-weight: 400;
|
|
266
|
+
line-height: 22px;
|
|
267
|
+
color: var(--odn-color-black-12);
|
|
258
268
|
transition: border-color 0.15s, background 0.15s, color 0.15s;
|
|
259
269
|
}
|
|
260
270
|
[data-odn-skill-slot][data-odn-skill-slot-variant=bar] [data-odn-skill-slot-item]:not([data-odn-skill-slot-item-disabled]):hover {
|
|
261
|
-
border-color: var(--odn-color-black-
|
|
262
|
-
background: var(--odn-color-black-3);
|
|
263
|
-
color: var(--odn-color-black-12);
|
|
271
|
+
border-color: var(--odn-color-black-3);
|
|
264
272
|
}
|
|
265
273
|
[data-odn-skill-slot][data-odn-skill-slot-variant=bar] [data-odn-skill-slot-item]:not([data-odn-skill-slot-item-disabled]):active {
|
|
266
|
-
|
|
274
|
+
border-color: var(--odn-color-black-4);
|
|
267
275
|
}
|
|
268
276
|
[data-odn-skill-slot][data-odn-skill-slot-variant=bar] [data-odn-skill-slot-item]:not([data-odn-skill-slot-item-disabled]):focus-visible {
|
|
269
277
|
outline: none;
|
|
270
278
|
border-color: var(--odn-color-brand-6);
|
|
271
|
-
|
|
272
|
-
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* bar chip hover 的全文提示:暗色浮层(设计稿「文字提示」:向上、多行)。
|
|
282
|
+
* 整条 bar 走 Tooltip.Group,浮层是 tooltip;popupClassName 落在
|
|
283
|
+
* [data-odn-popup-content] 上,用 [data-type] 兜住 pop-base 的
|
|
284
|
+
* popover 白底 / tooltip #404040 两套默认值。 */
|
|
285
|
+
[data-odn-popup][data-type] [data-odn-popup-content].odn-skill-slot-bar-popup {
|
|
286
|
+
max-width: 400px;
|
|
287
|
+
padding: 4px 8px;
|
|
288
|
+
border-radius: 4px;
|
|
289
|
+
background-color: var(--odn-color-solid-black-11);
|
|
290
|
+
color: var(--odn-color-white);
|
|
291
|
+
font-size: 14px;
|
|
292
|
+
font-weight: 400;
|
|
293
|
+
line-height: 22px;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
[data-odn-popup][data-type] [data-odn-popup-content].odn-skill-slot-bar-popup [data-odn-popup-arrow-fill] {
|
|
297
|
+
fill: var(--odn-color-solid-black-11);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* 浮层纯视觉遮挡,不接收鼠标:pointer-events 可继承,给外层一次即可。
|
|
301
|
+
* 多行 bar 里浮层会压在上一行的块上,若吃掉事件,块↔块之间就换不过去,
|
|
302
|
+
* 也会挡住下层页面/输入框。 */
|
|
303
|
+
[data-odn-popup]:has(> [data-odn-popup-content].odn-skill-slot-bar-popup) {
|
|
304
|
+
pointer-events: none;
|
|
273
305
|
}
|
|
274
306
|
|
|
275
307
|
/* ---- variant: card ---- */
|
|
@@ -366,4 +398,12 @@
|
|
|
366
398
|
color: var(--odn-color-black-9);
|
|
367
399
|
word-break: break-word;
|
|
368
400
|
white-space: pre-wrap;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* bar 的全文浮层里换成暗底白字 14/22(宽度由 .odn-skill-slot-bar-popup 的 400 兜住) */
|
|
404
|
+
.odn-skill-slot-bar-popup [data-odn-skill-slot-desc-full] {
|
|
405
|
+
max-width: 100%;
|
|
406
|
+
font-size: 14px;
|
|
407
|
+
line-height: 22px;
|
|
408
|
+
color: var(--odn-color-white);
|
|
369
409
|
}
|