one-design-next 0.0.82 → 0.0.83
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/index.js
CHANGED
|
@@ -894,7 +894,9 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
|
|
|
894
894
|
},
|
|
895
895
|
popupClassName: "odn-composer-skill-popup",
|
|
896
896
|
popup: /*#__PURE__*/React.createElement(SkillSlot, {
|
|
897
|
+
key: toolMenuOpen ? 'open' : 'closed',
|
|
897
898
|
variant: "menu",
|
|
899
|
+
searchable: true,
|
|
898
900
|
skills: skillGroups ? undefined : skills,
|
|
899
901
|
skillGroups: skillGroups,
|
|
900
902
|
showGroupTitles: showGroupTitles,
|
|
@@ -63,6 +63,11 @@ export interface SkillSlotProps {
|
|
|
63
63
|
* 不传则保持默认布局宽度。
|
|
64
64
|
*/
|
|
65
65
|
itemWidth?: number;
|
|
66
|
+
/**
|
|
67
|
+
* menu 变体:在列表顶部显示搜索框,按 label / description 即时过滤,命中字高亮。
|
|
68
|
+
* bar / card 忽略。本轮不做搜索框内的键盘导航。
|
|
69
|
+
*/
|
|
70
|
+
searchable?: boolean;
|
|
66
71
|
className?: string;
|
|
67
72
|
style?: React.CSSProperties;
|
|
68
73
|
}
|
package/dist/skill-slot/index.js
CHANGED
|
@@ -4,8 +4,17 @@ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
|
4
4
|
import { Fragment, forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import HoverFill from "../hover-fill";
|
|
6
6
|
import Icon from "../icon";
|
|
7
|
+
import Input from "../input";
|
|
7
8
|
import Popover from "../popover";
|
|
9
|
+
import { highlightQueryText } from "../stream-text/highlight-query-text";
|
|
8
10
|
import "./style";
|
|
11
|
+
function skillMatchesQuery(skill, query) {
|
|
12
|
+
var _skill$description;
|
|
13
|
+
var q = query.trim().toLowerCase();
|
|
14
|
+
if (!q) return true;
|
|
15
|
+
if (skill.label.toLowerCase().includes(q)) return true;
|
|
16
|
+
return ((_skill$description = skill.description) !== null && _skill$description !== void 0 ? _skill$description : '').toLowerCase().includes(q);
|
|
17
|
+
}
|
|
9
18
|
|
|
10
19
|
/* 给 listbox / option 生成稳定且唯一的 id 前缀(aria-activedescendant 需要)。
|
|
11
20
|
* 模块级自增 + useState 初始化,保证每个实例 mount 期间 id 稳定。 */
|
|
@@ -16,7 +25,8 @@ var skillSlotIdSeq = 0;
|
|
|
16
25
|
* 始终挂载 Popover(用 visible 关掉未截断态),避免条件挂载导致 ref/测量丢帧。
|
|
17
26
|
*/
|
|
18
27
|
function SkillDescPopover(_ref) {
|
|
19
|
-
var description = _ref.description
|
|
28
|
+
var description = _ref.description,
|
|
29
|
+
highlightQuery = _ref.highlightQuery;
|
|
20
30
|
var ref = useRef(null);
|
|
21
31
|
var _useState = useState(false),
|
|
22
32
|
_useState2 = _slicedToArray(_useState, 2),
|
|
@@ -59,11 +69,11 @@ function SkillDescPopover(_ref) {
|
|
|
59
69
|
visible: truncated ? undefined : false,
|
|
60
70
|
popup: /*#__PURE__*/React.createElement("span", {
|
|
61
71
|
"data-odn-skill-slot-desc-full": true
|
|
62
|
-
}, description)
|
|
72
|
+
}, highlightQueryText(description, highlightQuery))
|
|
63
73
|
}, /*#__PURE__*/React.createElement("span", {
|
|
64
74
|
ref: ref,
|
|
65
75
|
"data-odn-skill-slot-item-desc": true
|
|
66
|
-
}, description));
|
|
76
|
+
}, highlightQueryText(description, highlightQuery)));
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
/** 命令式句柄:供"焦点不在 menu 上"的宿主(如 composer 的 contenteditable)转发键盘。 */
|
|
@@ -82,15 +92,53 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
82
92
|
menuMaxHeight = _ref2.menuMaxHeight,
|
|
83
93
|
menuWidth = _ref2.menuWidth,
|
|
84
94
|
itemWidth = _ref2.itemWidth,
|
|
95
|
+
_ref2$searchable = _ref2.searchable,
|
|
96
|
+
searchable = _ref2$searchable === void 0 ? false : _ref2$searchable,
|
|
85
97
|
className = _ref2.className,
|
|
86
98
|
style = _ref2.style;
|
|
87
|
-
|
|
88
|
-
var
|
|
99
|
+
var searchableMenu = variant === 'menu' && searchable;
|
|
100
|
+
var _useState3 = useState(''),
|
|
101
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
102
|
+
searchInput = _useState4[0],
|
|
103
|
+
setSearchInput = _useState4[1];
|
|
104
|
+
var _useState5 = useState(''),
|
|
105
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
106
|
+
searchQuery = _useState6[0],
|
|
107
|
+
setSearchQuery = _useState6[1];
|
|
108
|
+
var composingRef = useRef(false);
|
|
109
|
+
|
|
110
|
+
/* 目录全集:过滤前的列表。目录为空才整棵不渲染。 */
|
|
111
|
+
var catalogSkills = useMemo(function () {
|
|
89
112
|
if (skillGroups) return skillGroups.flatMap(function (g) {
|
|
90
113
|
return g.skills;
|
|
91
114
|
});
|
|
92
115
|
return skillsProp !== null && skillsProp !== void 0 ? skillsProp : [];
|
|
93
116
|
}, [skillGroups, skillsProp]);
|
|
117
|
+
var visibleGroups = useMemo(function () {
|
|
118
|
+
if (!skillGroups) return undefined;
|
|
119
|
+
if (!searchableMenu) return skillGroups;
|
|
120
|
+
return skillGroups.map(function (g) {
|
|
121
|
+
return {
|
|
122
|
+
groupName: g.groupName,
|
|
123
|
+
skills: g.skills.filter(function (s) {
|
|
124
|
+
return skillMatchesQuery(s, searchQuery);
|
|
125
|
+
})
|
|
126
|
+
};
|
|
127
|
+
}).filter(function (g) {
|
|
128
|
+
return g.skills.length > 0;
|
|
129
|
+
});
|
|
130
|
+
}, [skillGroups, searchableMenu, searchQuery]);
|
|
131
|
+
|
|
132
|
+
/* 优先使用过滤后的 skillGroups,否则回退到 skills。 */
|
|
133
|
+
var skills = useMemo(function () {
|
|
134
|
+
if (visibleGroups) return visibleGroups.flatMap(function (g) {
|
|
135
|
+
return g.skills;
|
|
136
|
+
});
|
|
137
|
+
if (!searchableMenu) return catalogSkills;
|
|
138
|
+
return catalogSkills.filter(function (s) {
|
|
139
|
+
return skillMatchesQuery(s, searchQuery);
|
|
140
|
+
});
|
|
141
|
+
}, [visibleGroups, catalogSkills, searchableMenu, searchQuery]);
|
|
94
142
|
var showGroupTitles = showGroupTitlesProp !== null && showGroupTitlesProp !== void 0 ? showGroupTitlesProp : variant === 'menu';
|
|
95
143
|
var groupedWithTitles = !!(skillGroups && showGroupTitles);
|
|
96
144
|
/* menu 变体:HoverFill 完全只管鼠标交互(命令式状态机,不变);键盘 ↑↓
|
|
@@ -110,15 +158,15 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
110
158
|
* 维护高亮 index,监听 ↑↓ / Home / End 移动、Enter / Space 选中,
|
|
111
159
|
* 并自动跳过 disabled 项、把高亮项滚动进视口。 */
|
|
112
160
|
|
|
113
|
-
var
|
|
161
|
+
var _useState7 = useState(function () {
|
|
114
162
|
return "odn-skill-slot-".concat(++skillSlotIdSeq);
|
|
115
163
|
}),
|
|
116
|
-
|
|
117
|
-
listboxId =
|
|
118
|
-
var
|
|
119
|
-
|
|
120
|
-
internalActiveIndex =
|
|
121
|
-
setInternalActiveIndex =
|
|
164
|
+
_useState8 = _slicedToArray(_useState7, 1),
|
|
165
|
+
listboxId = _useState8[0];
|
|
166
|
+
var _useState9 = useState(-1),
|
|
167
|
+
_useState10 = _slicedToArray(_useState9, 2),
|
|
168
|
+
internalActiveIndex = _useState10[0],
|
|
169
|
+
setInternalActiveIndex = _useState10[1];
|
|
122
170
|
var isControlled = activeIndex !== undefined && activeIndex !== null;
|
|
123
171
|
var currentIndex = isControlled ? activeIndex : internalActiveIndex;
|
|
124
172
|
var itemRefs = useRef([]);
|
|
@@ -230,7 +278,7 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
230
278
|
if (e.currentTarget.contains(e.relatedTarget)) return;
|
|
231
279
|
setInternalActiveIndex(-1);
|
|
232
280
|
};
|
|
233
|
-
if (
|
|
281
|
+
if (catalogSkills.length === 0) return null;
|
|
234
282
|
|
|
235
283
|
/* menu variant 有 scroll-wrap 时,滚动仅包裹技能列表,footer 固定在底部不参与滚动。 */
|
|
236
284
|
var hasScrollWrap = variant === 'menu' && menuMaxHeight !== undefined && menuMaxHeight !== null;
|
|
@@ -279,8 +327,9 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
279
327
|
"data-odn-skill-slot-item-content": true
|
|
280
328
|
}, /*#__PURE__*/React.createElement("span", {
|
|
281
329
|
"data-odn-skill-slot-item-label": true
|
|
282
|
-
}, skill.label), skill.description ? /*#__PURE__*/React.createElement(SkillDescPopover, {
|
|
283
|
-
description: skill.description
|
|
330
|
+
}, highlightQueryText(skill.label, searchableMenu ? searchQuery : undefined)), skill.description ? /*#__PURE__*/React.createElement(SkillDescPopover, {
|
|
331
|
+
description: skill.description,
|
|
332
|
+
highlightQuery: searchableMenu ? searchQuery : undefined
|
|
284
333
|
}) : null)));
|
|
285
334
|
};
|
|
286
335
|
|
|
@@ -365,7 +414,7 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
365
414
|
var renderGroups = function renderGroups() {
|
|
366
415
|
var flatIndex = 0;
|
|
367
416
|
if (variant === 'menu') {
|
|
368
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null,
|
|
417
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, visibleGroups.map(function (group, groupIdx) {
|
|
369
418
|
return /*#__PURE__*/React.createElement(Fragment, {
|
|
370
419
|
key: group.groupName || "_group_".concat(groupIdx)
|
|
371
420
|
}, groupIdx > 0 ? /*#__PURE__*/React.createElement("div", {
|
|
@@ -378,7 +427,7 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
378
427
|
}));
|
|
379
428
|
}));
|
|
380
429
|
}
|
|
381
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null,
|
|
430
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, visibleGroups.map(function (group, groupIdx) {
|
|
382
431
|
return /*#__PURE__*/React.createElement("div", {
|
|
383
432
|
key: group.groupName || "_group_".concat(groupIdx),
|
|
384
433
|
"data-odn-skill-slot-group": true
|
|
@@ -403,8 +452,41 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
403
452
|
|
|
404
453
|
/* 有 skillGroups:始终按组遍历(保序);无标题时仍走分组 DOM,仅不渲染 header,
|
|
405
454
|
* bar/card 用 group-items 承接原有 flex/grid,避免拍平后丢组边界。 */
|
|
406
|
-
var listBody =
|
|
455
|
+
var listBody = visibleGroups ? renderGroups() : renderFlatItems();
|
|
407
456
|
var manageFooter = renderManageFooter();
|
|
457
|
+
var searchBar = searchableMenu ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
458
|
+
"data-odn-skill-slot-search": true,
|
|
459
|
+
onCompositionStart: function onCompositionStart() {
|
|
460
|
+
composingRef.current = true;
|
|
461
|
+
},
|
|
462
|
+
onCompositionEnd: function onCompositionEnd(e) {
|
|
463
|
+
composingRef.current = false;
|
|
464
|
+
var value = e.target instanceof HTMLInputElement ? e.target.value : searchInput;
|
|
465
|
+
setSearchInput(value);
|
|
466
|
+
setSearchQuery(value);
|
|
467
|
+
}
|
|
468
|
+
}, /*#__PURE__*/React.createElement(Input, {
|
|
469
|
+
light: true,
|
|
470
|
+
size: "small",
|
|
471
|
+
allowClear: true,
|
|
472
|
+
placeholder: "\u641C\u7D22\u6280\u80FD\u540D\u79F0\u6216\u63CF\u8FF0",
|
|
473
|
+
value: searchInput,
|
|
474
|
+
onChange: function onChange(e, value) {
|
|
475
|
+
setSearchInput(value);
|
|
476
|
+
var composing = composingRef.current || e.nativeEvent.isComposing;
|
|
477
|
+
if (composing) return;
|
|
478
|
+
setSearchQuery(value);
|
|
479
|
+
},
|
|
480
|
+
leftElement: /*#__PURE__*/React.createElement(Icon, {
|
|
481
|
+
name: "search",
|
|
482
|
+
size: 14
|
|
483
|
+
})
|
|
484
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
485
|
+
"data-odn-skill-slot-divider": true
|
|
486
|
+
})) : null;
|
|
487
|
+
var emptyState = searchableMenu && skills.length === 0 ? /*#__PURE__*/React.createElement("div", {
|
|
488
|
+
"data-odn-skill-slot-empty": true
|
|
489
|
+
}, "\u672A\u627E\u5230\u76F8\u5173\u6280\u80FD") : null;
|
|
408
490
|
|
|
409
491
|
/* 共享的属性容器:menu variant 的 aria / keyboard / 交互属性 */
|
|
410
492
|
var listAttrs = variant === 'menu' ? {
|
|
@@ -421,29 +503,33 @@ export var SkillSlot = /*#__PURE__*/forwardRef(function SkillSlot(_ref2, ref) {
|
|
|
421
503
|
} : {}), variant !== 'menu' && itemWidth !== undefined && itemWidth !== null ? {
|
|
422
504
|
'--odn-skill-slot-item-width': "".concat(itemWidth, "px")
|
|
423
505
|
} : {}), style);
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
return /*#__PURE__*/React.createElement("div", _extends({
|
|
506
|
+
if (variant !== 'menu') {
|
|
507
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
427
508
|
"data-odn-skill-slot": true,
|
|
428
509
|
"data-odn-skill-slot-variant": variant,
|
|
429
510
|
"data-odn-skill-slot-grouped": groupedWithTitles || undefined,
|
|
430
511
|
className: className,
|
|
431
512
|
style: rootStyle
|
|
432
|
-
},
|
|
433
|
-
"data-odn-skill-slot-scroll-wrap": true,
|
|
434
|
-
style: {
|
|
435
|
-
maxHeight: menuMaxHeight,
|
|
436
|
-
overflowY: 'auto'
|
|
437
|
-
}
|
|
438
|
-
}, listBody), manageFooter);
|
|
513
|
+
}, listBody);
|
|
439
514
|
}
|
|
440
|
-
|
|
515
|
+
|
|
516
|
+
/* listbox 挂在列表容器上,搜索框 / footer 不进 listbox。 */
|
|
517
|
+
var menuList = /*#__PURE__*/React.createElement("div", _extends({
|
|
518
|
+
"data-odn-skill-slot-list": true
|
|
519
|
+
}, listAttrs), skills.length > 0 ? listBody : emptyState);
|
|
520
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
441
521
|
"data-odn-skill-slot": true,
|
|
442
522
|
"data-odn-skill-slot-variant": variant,
|
|
443
523
|
"data-odn-skill-slot-grouped": groupedWithTitles || undefined,
|
|
444
524
|
className: className,
|
|
445
525
|
style: rootStyle
|
|
446
|
-
},
|
|
526
|
+
}, searchBar, hasScrollWrap ? /*#__PURE__*/React.createElement("div", {
|
|
527
|
+
"data-odn-skill-slot-scroll-wrap": true,
|
|
528
|
+
style: {
|
|
529
|
+
maxHeight: menuMaxHeight,
|
|
530
|
+
overflowY: 'auto'
|
|
531
|
+
}
|
|
532
|
+
}, menuList) : menuList, manageFooter);
|
|
447
533
|
});
|
|
448
534
|
SkillSlot.displayName = 'SkillSlot';
|
|
449
535
|
export default SkillSlot;
|
|
@@ -41,6 +41,76 @@
|
|
|
41
41
|
outline: none;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
[data-odn-skill-slot-list] {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
width: 100%;
|
|
48
|
+
}
|
|
49
|
+
[data-odn-skill-slot-list]:focus, [data-odn-skill-slot-list]:focus-visible {
|
|
50
|
+
outline: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* 顶栏滤镜行:与底部「技能管理」同构(无描边、热区不拉通栏)。
|
|
54
|
+
* 左右 4px 与菜单上下 padding 对齐;选择器压过 Input 默认描边 / 聚焦蓝框。 */
|
|
55
|
+
[data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-search] {
|
|
56
|
+
flex-shrink: 0;
|
|
57
|
+
margin: 0 4px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
[data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-search] [data-odn-input] {
|
|
61
|
+
display: flex;
|
|
62
|
+
width: 100%;
|
|
63
|
+
min-width: 0;
|
|
64
|
+
box-sizing: border-box;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 8px;
|
|
67
|
+
padding: 8px 12px;
|
|
68
|
+
font-size: 13px;
|
|
69
|
+
line-height: 22px;
|
|
70
|
+
color: var(--odn-color-black-12);
|
|
71
|
+
border: none;
|
|
72
|
+
border-radius: 4px;
|
|
73
|
+
background: transparent;
|
|
74
|
+
box-shadow: none;
|
|
75
|
+
outline: none;
|
|
76
|
+
transition: background-color 0.15s;
|
|
77
|
+
}
|
|
78
|
+
[data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-search] [data-odn-input][data-odn-input-focused=true] {
|
|
79
|
+
border: none;
|
|
80
|
+
box-shadow: none;
|
|
81
|
+
outline: none;
|
|
82
|
+
background: transparent;
|
|
83
|
+
}
|
|
84
|
+
[data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-search] [data-odn-input]:hover {
|
|
85
|
+
background: rgba(33, 34, 38, 0.05);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
[data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-search] [data-odn-input-left-element] {
|
|
89
|
+
color: var(--odn-color-black-9);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
[data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-search-mark] {
|
|
93
|
+
background: transparent;
|
|
94
|
+
color: var(--odn-color-brand-6);
|
|
95
|
+
padding: 0;
|
|
96
|
+
margin: 0;
|
|
97
|
+
border: 0;
|
|
98
|
+
border-radius: 0;
|
|
99
|
+
font: inherit;
|
|
100
|
+
letter-spacing: inherit;
|
|
101
|
+
word-spacing: inherit;
|
|
102
|
+
line-height: inherit;
|
|
103
|
+
vertical-align: baseline;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[data-odn-skill-slot-empty] {
|
|
107
|
+
padding: 20px 12px;
|
|
108
|
+
font-size: 13px;
|
|
109
|
+
line-height: 20px;
|
|
110
|
+
color: var(--odn-color-black-8);
|
|
111
|
+
text-align: center;
|
|
112
|
+
}
|
|
113
|
+
|
|
44
114
|
[data-odn-skill-slot][data-odn-skill-slot-variant=menu] [data-odn-skill-slot-item-wrap] {
|
|
45
115
|
display: block;
|
|
46
116
|
}
|
|
@@ -171,9 +241,11 @@
|
|
|
171
241
|
display: flex;
|
|
172
242
|
align-items: center;
|
|
173
243
|
justify-content: space-between;
|
|
174
|
-
|
|
244
|
+
box-sizing: border-box;
|
|
245
|
+
margin: 0 4px;
|
|
175
246
|
padding: 8px 12px;
|
|
176
247
|
border: none;
|
|
248
|
+
border-radius: 4px;
|
|
177
249
|
background: transparent;
|
|
178
250
|
font-size: 13px;
|
|
179
251
|
color: var(--odn-color-black-9);
|