@use-kona/editor 0.1.17 → 0.1.18
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/plugins/CommandsPlugin/Menu.js +5 -18
- package/dist/plugins/CommandsPlugin/resolveCommands.d.ts +0 -1
- package/dist/plugins/CommandsPlugin/resolveCommands.js +21 -30
- package/dist/plugins/CommandsPlugin/resolveCommands.spec.js +58 -12
- package/dist/plugins/CommandsPlugin/styles.module.js +0 -2
- package/dist/plugins/CommandsPlugin/styles_module.css +2 -18
- package/dist/plugins/CommandsPlugin/useResolvedCommands.js +23 -3
- package/package.json +1 -1
- package/src/plugins/CommandsPlugin/Menu.tsx +5 -13
- package/src/plugins/CommandsPlugin/resolveCommands.spec.ts +30 -13
- package/src/plugins/CommandsPlugin/resolveCommands.ts +40 -38
- package/src/plugins/CommandsPlugin/styles.module.css +2 -18
- package/src/plugins/CommandsPlugin/useResolvedCommands.ts +28 -7
|
@@ -23,7 +23,6 @@ const Menu = (props)=>{
|
|
|
23
23
|
match: (n)=>Editor.isBlock(editor, n)
|
|
24
24
|
});
|
|
25
25
|
const isBrowseMode = 'string' == typeof store.filter && '' === store.filter;
|
|
26
|
-
const isSearchMode = 'string' == typeof store.filter && '' !== store.filter;
|
|
27
26
|
const { commands, isLoading, isError } = useResolvedCommands({
|
|
28
27
|
rootCommands,
|
|
29
28
|
filter: store.filter,
|
|
@@ -67,9 +66,8 @@ const Menu = (props)=>{
|
|
|
67
66
|
store.openId
|
|
68
67
|
]);
|
|
69
68
|
useEffect(()=>{
|
|
70
|
-
if (false === store.filter ||
|
|
69
|
+
if (false === store.filter || 'string' == typeof store.filter && '' !== store.filter) setActive(0);
|
|
71
70
|
}, [
|
|
72
|
-
isSearchMode,
|
|
73
71
|
store.filter
|
|
74
72
|
]);
|
|
75
73
|
useEffect(()=>{
|
|
@@ -199,7 +197,6 @@ const Menu = (props)=>{
|
|
|
199
197
|
const hasRows = entries.length > 0 || isLoading || isError;
|
|
200
198
|
if (false === store.filter || !hasRows) return null;
|
|
201
199
|
if (entry && ignoreNodes.includes(entry[0].type)) return null;
|
|
202
|
-
const pathLabel = path.map((item)=>item.title).join(' / ');
|
|
203
200
|
return /*#__PURE__*/ createPortal(renderMenu(/*#__PURE__*/ jsxs(Fragment, {
|
|
204
201
|
children: [
|
|
205
202
|
store.isOpen && /*#__PURE__*/ jsx("div", {
|
|
@@ -216,10 +213,6 @@ const Menu = (props)=>{
|
|
|
216
213
|
event.preventDefault();
|
|
217
214
|
},
|
|
218
215
|
children: [
|
|
219
|
-
isBrowseMode && pathLabel && /*#__PURE__*/ jsx("div", {
|
|
220
|
-
className: styles_module.path,
|
|
221
|
-
children: pathLabel
|
|
222
|
-
}),
|
|
223
216
|
entries.map((entry, index)=>{
|
|
224
217
|
if ('back' === entry.type) return /*#__PURE__*/ jsxs("button", {
|
|
225
218
|
type: "button",
|
|
@@ -271,17 +264,11 @@ const Menu = (props)=>{
|
|
|
271
264
|
className: styles_module.icon,
|
|
272
265
|
children: entry.command.command.icon
|
|
273
266
|
}),
|
|
274
|
-
/*#__PURE__*/
|
|
267
|
+
/*#__PURE__*/ jsx("span", {
|
|
275
268
|
className: styles_module.content,
|
|
276
|
-
children:
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}),
|
|
280
|
-
isSearchMode && entry.command.breadcrumb && /*#__PURE__*/ jsx("span", {
|
|
281
|
-
className: styles_module.breadcrumb,
|
|
282
|
-
children: entry.command.breadcrumb
|
|
283
|
-
})
|
|
284
|
-
]
|
|
269
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
270
|
+
children: entry.command.command.title
|
|
271
|
+
})
|
|
285
272
|
}),
|
|
286
273
|
entry.command.isSubmenu && isBrowseMode && /*#__PURE__*/ jsx("span", {
|
|
287
274
|
className: styles_module.submenu,
|
|
@@ -18,17 +18,21 @@ const toResolvedCommand = (command, parentPath)=>{
|
|
|
18
18
|
command,
|
|
19
19
|
path,
|
|
20
20
|
key: pathToKey(path),
|
|
21
|
-
breadcrumb: parentPath.map((item)=>item.title).join(' / '),
|
|
22
21
|
isSubmenu: Boolean(command.getCommands)
|
|
23
22
|
};
|
|
24
23
|
};
|
|
25
|
-
const
|
|
24
|
+
const resolveCurrentLevelCommands = async (params, resolveChildCommands, queryForCurrentLevel)=>{
|
|
26
25
|
const { rootCommands, path, editor } = params;
|
|
27
26
|
let currentCommands = rootCommands;
|
|
28
27
|
let currentPath = [];
|
|
29
|
-
for
|
|
28
|
+
for(let index = 0; index < path.length; index++){
|
|
29
|
+
const item = path[index];
|
|
30
|
+
const isLastPathItem = index === path.length - 1;
|
|
30
31
|
const command = currentCommands.find((entry)=>entry.name === item.name);
|
|
31
|
-
if (!command?.getCommands) return
|
|
32
|
+
if (!command?.getCommands) return {
|
|
33
|
+
commands: [],
|
|
34
|
+
path: currentPath
|
|
35
|
+
};
|
|
32
36
|
currentPath = [
|
|
33
37
|
...currentPath,
|
|
34
38
|
toPathEntry(command)
|
|
@@ -36,37 +40,24 @@ const resolveBrowseCommands = async (params, resolveChildCommands)=>{
|
|
|
36
40
|
currentCommands = await resolveChildCommands({
|
|
37
41
|
command,
|
|
38
42
|
path: currentPath,
|
|
39
|
-
query: '',
|
|
43
|
+
query: isLastPathItem ? queryForCurrentLevel : '',
|
|
40
44
|
editor
|
|
41
45
|
});
|
|
42
46
|
}
|
|
43
|
-
return
|
|
47
|
+
return {
|
|
48
|
+
commands: currentCommands,
|
|
49
|
+
path: currentPath
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
const resolveBrowseCommands = async (params, resolveChildCommands)=>{
|
|
53
|
+
const currentLevel = await resolveCurrentLevelCommands(params, resolveChildCommands, '');
|
|
54
|
+
return currentLevel.commands.map((command)=>toResolvedCommand(command, currentLevel.path));
|
|
44
55
|
};
|
|
45
56
|
const resolveSearchCommands = async (params, resolveChildCommands)=>{
|
|
46
|
-
const {
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
if (command.getCommands) {
|
|
51
|
-
if (isCommandMatchesQuery(command, filter)) commands.push(toResolvedCommand(command, parentPath));
|
|
52
|
-
const currentPath = [
|
|
53
|
-
...parentPath,
|
|
54
|
-
toPathEntry(command)
|
|
55
|
-
];
|
|
56
|
-
const children = await resolveChildCommands({
|
|
57
|
-
command,
|
|
58
|
-
path: currentPath,
|
|
59
|
-
query: filter,
|
|
60
|
-
editor
|
|
61
|
-
});
|
|
62
|
-
await walk(children, currentPath);
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
if (command.action && isCommandMatchesQuery(command, filter)) commands.push(toResolvedCommand(command, parentPath));
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
await walk(rootCommands, []);
|
|
69
|
-
return commands;
|
|
57
|
+
const { filter } = params;
|
|
58
|
+
const currentLevel = await resolveCurrentLevelCommands(params, resolveChildCommands, filter);
|
|
59
|
+
const matchedCommands = currentLevel.commands.filter((command)=>isCommandMatchesQuery(command, filter));
|
|
60
|
+
return matchedCommands.map((command)=>toResolvedCommand(command, currentLevel.path));
|
|
70
61
|
};
|
|
71
62
|
class CommandsResolver {
|
|
72
63
|
cache = new Map();
|
|
@@ -110,7 +110,7 @@ describe('CommandsResolver', ()=>{
|
|
|
110
110
|
expect(result.commands).toHaveLength(1);
|
|
111
111
|
expect(result.commands[0]?.command.name).toBe('code');
|
|
112
112
|
});
|
|
113
|
-
it('
|
|
113
|
+
it('searches only root level when path is empty', async ()=>{
|
|
114
114
|
const resolver = new CommandsResolver();
|
|
115
115
|
const rootCommands = [
|
|
116
116
|
{
|
|
@@ -120,14 +120,37 @@ describe('CommandsResolver', ()=>{
|
|
|
120
120
|
icon: null,
|
|
121
121
|
getCommands: ()=>[
|
|
122
122
|
leaf({
|
|
123
|
-
name: '
|
|
124
|
-
title: '
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
name: 'code',
|
|
124
|
+
title: 'Code'
|
|
125
|
+
})
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
leaf({
|
|
129
|
+
name: 'paragraph',
|
|
130
|
+
title: 'Paragraph'
|
|
131
|
+
})
|
|
132
|
+
];
|
|
133
|
+
const request = resolver.resolve({
|
|
134
|
+
rootCommands,
|
|
135
|
+
filter: 'code',
|
|
136
|
+
path: [],
|
|
137
|
+
editor
|
|
138
|
+
});
|
|
139
|
+
const result = await request.promise;
|
|
140
|
+
expect(result.commands).toHaveLength(0);
|
|
141
|
+
});
|
|
142
|
+
it('searches only current nested level', async ()=>{
|
|
143
|
+
const resolver = new CommandsResolver();
|
|
144
|
+
const rootCommands = [
|
|
145
|
+
{
|
|
146
|
+
name: 'insert',
|
|
147
|
+
title: 'Insert',
|
|
148
|
+
commandName: 'insert',
|
|
149
|
+
icon: null,
|
|
150
|
+
getCommands: ()=>[
|
|
127
151
|
leaf({
|
|
128
152
|
name: 'code',
|
|
129
|
-
title: 'Code'
|
|
130
|
-
commandName: 'code'
|
|
153
|
+
title: 'Code'
|
|
131
154
|
})
|
|
132
155
|
]
|
|
133
156
|
}
|
|
@@ -135,13 +158,18 @@ describe('CommandsResolver', ()=>{
|
|
|
135
158
|
const request = resolver.resolve({
|
|
136
159
|
rootCommands,
|
|
137
160
|
filter: 'code',
|
|
138
|
-
path: [
|
|
161
|
+
path: [
|
|
162
|
+
{
|
|
163
|
+
name: 'insert',
|
|
164
|
+
title: 'Insert',
|
|
165
|
+
commandName: 'insert'
|
|
166
|
+
}
|
|
167
|
+
],
|
|
139
168
|
editor
|
|
140
169
|
});
|
|
141
170
|
const result = await request.promise;
|
|
142
171
|
expect(result.commands).toHaveLength(1);
|
|
143
172
|
expect(result.commands[0]?.command.name).toBe('code');
|
|
144
|
-
expect(result.commands[0]?.breadcrumb).toBe('Insert');
|
|
145
173
|
});
|
|
146
174
|
it('returns matching submenu commands in query mode', async ()=>{
|
|
147
175
|
const resolver = new CommandsResolver();
|
|
@@ -189,13 +217,25 @@ describe('CommandsResolver', ()=>{
|
|
|
189
217
|
const firstRequest = resolver.resolve({
|
|
190
218
|
rootCommands,
|
|
191
219
|
filter: 'a',
|
|
192
|
-
path: [
|
|
220
|
+
path: [
|
|
221
|
+
{
|
|
222
|
+
name: 'remote',
|
|
223
|
+
title: 'Remote',
|
|
224
|
+
commandName: 'remote'
|
|
225
|
+
}
|
|
226
|
+
],
|
|
193
227
|
editor
|
|
194
228
|
});
|
|
195
229
|
const secondRequest = resolver.resolve({
|
|
196
230
|
rootCommands,
|
|
197
231
|
filter: 'b',
|
|
198
|
-
path: [
|
|
232
|
+
path: [
|
|
233
|
+
{
|
|
234
|
+
name: 'remote',
|
|
235
|
+
title: 'Remote',
|
|
236
|
+
commandName: 'remote'
|
|
237
|
+
}
|
|
238
|
+
],
|
|
199
239
|
editor
|
|
200
240
|
});
|
|
201
241
|
searchB.resolve([
|
|
@@ -257,7 +297,13 @@ describe('CommandsResolver', ()=>{
|
|
|
257
297
|
const loadingRequest = resolver.resolve({
|
|
258
298
|
rootCommands,
|
|
259
299
|
filter: 'code',
|
|
260
|
-
path: [
|
|
300
|
+
path: [
|
|
301
|
+
{
|
|
302
|
+
name: 'remote',
|
|
303
|
+
title: 'Remote',
|
|
304
|
+
commandName: 'remote'
|
|
305
|
+
}
|
|
306
|
+
],
|
|
261
307
|
editor
|
|
262
308
|
});
|
|
263
309
|
expect(loadingRequest.state.isLoading).toBe(true);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
will-change: transform, opacity;
|
|
9
9
|
contain: layout paint style;
|
|
10
10
|
backface-visibility: hidden;
|
|
11
|
-
border-radius:
|
|
11
|
+
border-radius: 8px;
|
|
12
12
|
flex-direction: column;
|
|
13
13
|
max-height: 200px;
|
|
14
14
|
margin-top: -6px;
|
|
@@ -70,32 +70,16 @@
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.content-VZW7lK {
|
|
73
|
-
flex-direction:
|
|
73
|
+
flex-direction: row;
|
|
74
74
|
flex: 1;
|
|
75
|
-
row-gap: 2px;
|
|
76
75
|
min-width: 0;
|
|
77
76
|
display: flex;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
.breadcrumb-Jr0wXh {
|
|
81
|
-
color: var(--kona-editor-secondary-text-color, #777);
|
|
82
|
-
white-space: nowrap;
|
|
83
|
-
text-overflow: ellipsis;
|
|
84
|
-
font-size: 11px;
|
|
85
|
-
overflow: hidden;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
79
|
.submenu-zNsqOg {
|
|
89
80
|
color: var(--kona-editor-secondary-text-color, #777);
|
|
90
81
|
}
|
|
91
82
|
|
|
92
|
-
.path-fYY_14 {
|
|
93
|
-
color: var(--kona-editor-secondary-text-color, #777);
|
|
94
|
-
border-bottom: 1px solid var(--kona-editor-border-color, #ddd);
|
|
95
|
-
padding: 6px 8px;
|
|
96
|
-
font-size: 11px;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
83
|
.systemRow-ln5twO {
|
|
100
84
|
min-height: 32px;
|
|
101
85
|
color: var(--kona-editor-secondary-text-color, #777);
|
|
@@ -10,14 +10,35 @@ const useResolvedCommands = (params)=>{
|
|
|
10
10
|
const { rootCommands, filter, path, editor, isOpen } = params;
|
|
11
11
|
const resolverRef = useRef(new CommandsResolver());
|
|
12
12
|
const [state, setState] = useState(EMPTY_STATE);
|
|
13
|
+
const prevIsOpenRef = useRef(false);
|
|
14
|
+
const prevPathKeyRef = useRef('');
|
|
15
|
+
const prevQueryRef = useRef('');
|
|
13
16
|
const query = 'string' == typeof filter ? filter : '';
|
|
14
17
|
useEffect(()=>{
|
|
15
|
-
if (!isOpen || false === filter)
|
|
16
|
-
|
|
18
|
+
if (!isOpen || false === filter) {
|
|
19
|
+
setState(EMPTY_STATE);
|
|
20
|
+
prevIsOpenRef.current = false;
|
|
21
|
+
prevPathKeyRef.current = '';
|
|
22
|
+
prevQueryRef.current = '';
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const pathKey = path.map((item)=>item.name).join('/');
|
|
26
|
+
const isNewSession = !prevIsOpenRef.current;
|
|
27
|
+
const isPathChanged = !isNewSession && prevPathKeyRef.current !== pathKey;
|
|
28
|
+
const isQueryChanged = !isNewSession && prevQueryRef.current !== query;
|
|
29
|
+
if (isNewSession || isPathChanged) setState({
|
|
17
30
|
commands: [],
|
|
18
31
|
isLoading: true,
|
|
19
32
|
isError: false
|
|
20
33
|
});
|
|
34
|
+
else if (isQueryChanged) setState((state)=>({
|
|
35
|
+
...state,
|
|
36
|
+
isLoading: true,
|
|
37
|
+
isError: false
|
|
38
|
+
}));
|
|
39
|
+
prevIsOpenRef.current = true;
|
|
40
|
+
prevPathKeyRef.current = pathKey;
|
|
41
|
+
prevQueryRef.current = query;
|
|
21
42
|
const timeout = window.setTimeout(()=>{
|
|
22
43
|
const request = resolverRef.current.resolve({
|
|
23
44
|
rootCommands,
|
|
@@ -25,7 +46,6 @@ const useResolvedCommands = (params)=>{
|
|
|
25
46
|
path,
|
|
26
47
|
editor
|
|
27
48
|
});
|
|
28
|
-
setState(request.state);
|
|
29
49
|
request.promise.then((resolved)=>{
|
|
30
50
|
setState(resolved);
|
|
31
51
|
});
|
package/package.json
CHANGED
|
@@ -44,7 +44,6 @@ export const Menu = (props: Props) => {
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
const isBrowseMode = typeof store.filter === 'string' && store.filter === '';
|
|
47
|
-
const isSearchMode = typeof store.filter === 'string' && store.filter !== '';
|
|
48
47
|
|
|
49
48
|
const { commands, isLoading, isError } = useResolvedCommands({
|
|
50
49
|
rootCommands,
|
|
@@ -87,10 +86,13 @@ export const Menu = (props: Props) => {
|
|
|
87
86
|
}, [store.isOpen, store.openId]);
|
|
88
87
|
|
|
89
88
|
useEffect(() => {
|
|
90
|
-
if (
|
|
89
|
+
if (
|
|
90
|
+
store.filter === false ||
|
|
91
|
+
(typeof store.filter === 'string' && store.filter !== '')
|
|
92
|
+
) {
|
|
91
93
|
setActive(0);
|
|
92
94
|
}
|
|
93
|
-
}, [
|
|
95
|
+
}, [store.filter]);
|
|
94
96
|
|
|
95
97
|
useEffect(() => {
|
|
96
98
|
if (!entries.length) {
|
|
@@ -268,8 +270,6 @@ export const Menu = (props: Props) => {
|
|
|
268
270
|
return null;
|
|
269
271
|
}
|
|
270
272
|
|
|
271
|
-
const pathLabel = path.map((item) => item.title).join(' / ');
|
|
272
|
-
|
|
273
273
|
return createPortal(
|
|
274
274
|
renderMenu(
|
|
275
275
|
<>
|
|
@@ -289,9 +289,6 @@ export const Menu = (props: Props) => {
|
|
|
289
289
|
event.preventDefault();
|
|
290
290
|
}}
|
|
291
291
|
>
|
|
292
|
-
{isBrowseMode && pathLabel && (
|
|
293
|
-
<div className={styles.path}>{pathLabel}</div>
|
|
294
|
-
)}
|
|
295
292
|
{entries.map((entry, index) => {
|
|
296
293
|
if (entry.type === 'back') {
|
|
297
294
|
return (
|
|
@@ -348,11 +345,6 @@ export const Menu = (props: Props) => {
|
|
|
348
345
|
</span>
|
|
349
346
|
<span className={styles.content}>
|
|
350
347
|
<span>{entry.command.command.title}</span>
|
|
351
|
-
{isSearchMode && entry.command.breadcrumb && (
|
|
352
|
-
<span className={styles.breadcrumb}>
|
|
353
|
-
{entry.command.breadcrumb}
|
|
354
|
-
</span>
|
|
355
|
-
)}
|
|
356
348
|
</span>
|
|
357
349
|
{entry.command.isSubmenu && isBrowseMode && (
|
|
358
350
|
<span className={styles.submenu} aria-hidden="true">
|
|
@@ -102,7 +102,7 @@ describe('CommandsResolver', () => {
|
|
|
102
102
|
expect(result.commands[0]?.command.name).toBe('code');
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
it('
|
|
105
|
+
it('searches only root level when path is empty', async () => {
|
|
106
106
|
const resolver = new CommandsResolver();
|
|
107
107
|
const rootCommands: Command[] = [
|
|
108
108
|
{
|
|
@@ -110,15 +110,9 @@ describe('CommandsResolver', () => {
|
|
|
110
110
|
title: 'Insert',
|
|
111
111
|
commandName: 'insert',
|
|
112
112
|
icon: null,
|
|
113
|
-
getCommands: () => [
|
|
114
|
-
leaf({
|
|
115
|
-
name: 'heading-1',
|
|
116
|
-
title: 'Heading 1',
|
|
117
|
-
commandName: 'heading1',
|
|
118
|
-
}),
|
|
119
|
-
leaf({ name: 'code', title: 'Code', commandName: 'code' }),
|
|
120
|
-
],
|
|
113
|
+
getCommands: () => [leaf({ name: 'code', title: 'Code' })],
|
|
121
114
|
},
|
|
115
|
+
leaf({ name: 'paragraph', title: 'Paragraph' }),
|
|
122
116
|
];
|
|
123
117
|
|
|
124
118
|
const request = resolver.resolve({
|
|
@@ -130,9 +124,32 @@ describe('CommandsResolver', () => {
|
|
|
130
124
|
|
|
131
125
|
const result = await request.promise;
|
|
132
126
|
|
|
127
|
+
expect(result.commands).toHaveLength(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('searches only current nested level', async () => {
|
|
131
|
+
const resolver = new CommandsResolver();
|
|
132
|
+
const rootCommands: Command[] = [
|
|
133
|
+
{
|
|
134
|
+
name: 'insert',
|
|
135
|
+
title: 'Insert',
|
|
136
|
+
commandName: 'insert',
|
|
137
|
+
icon: null,
|
|
138
|
+
getCommands: () => [leaf({ name: 'code', title: 'Code' })],
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
const request = resolver.resolve({
|
|
143
|
+
rootCommands,
|
|
144
|
+
filter: 'code',
|
|
145
|
+
path: [{ name: 'insert', title: 'Insert', commandName: 'insert' }],
|
|
146
|
+
editor,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const result = await request.promise;
|
|
150
|
+
|
|
133
151
|
expect(result.commands).toHaveLength(1);
|
|
134
152
|
expect(result.commands[0]?.command.name).toBe('code');
|
|
135
|
-
expect(result.commands[0]?.breadcrumb).toBe('Insert');
|
|
136
153
|
});
|
|
137
154
|
|
|
138
155
|
it('returns matching submenu commands in query mode', async () => {
|
|
@@ -185,13 +202,13 @@ describe('CommandsResolver', () => {
|
|
|
185
202
|
const firstRequest = resolver.resolve({
|
|
186
203
|
rootCommands,
|
|
187
204
|
filter: 'a',
|
|
188
|
-
path: [],
|
|
205
|
+
path: [{ name: 'remote', title: 'Remote', commandName: 'remote' }],
|
|
189
206
|
editor,
|
|
190
207
|
});
|
|
191
208
|
const secondRequest = resolver.resolve({
|
|
192
209
|
rootCommands,
|
|
193
210
|
filter: 'b',
|
|
194
|
-
path: [],
|
|
211
|
+
path: [{ name: 'remote', title: 'Remote', commandName: 'remote' }],
|
|
195
212
|
editor,
|
|
196
213
|
});
|
|
197
214
|
|
|
@@ -243,7 +260,7 @@ describe('CommandsResolver', () => {
|
|
|
243
260
|
const loadingRequest = resolver.resolve({
|
|
244
261
|
rootCommands,
|
|
245
262
|
filter: 'code',
|
|
246
|
-
path: [],
|
|
263
|
+
path: [{ name: 'remote', title: 'Remote', commandName: 'remote' }],
|
|
247
264
|
editor,
|
|
248
265
|
});
|
|
249
266
|
|
|
@@ -16,7 +16,6 @@ export type ResolvedCommand = {
|
|
|
16
16
|
command: Command;
|
|
17
17
|
key: string;
|
|
18
18
|
path: CommandPathEntry[];
|
|
19
|
-
breadcrumb: string;
|
|
20
19
|
isSubmenu: boolean;
|
|
21
20
|
};
|
|
22
21
|
|
|
@@ -84,36 +83,57 @@ const toResolvedCommand = (
|
|
|
84
83
|
command,
|
|
85
84
|
path,
|
|
86
85
|
key: pathToKey(path),
|
|
87
|
-
breadcrumb: parentPath.map((item) => item.title).join(' / '),
|
|
88
86
|
isSubmenu: Boolean(command.getCommands),
|
|
89
87
|
};
|
|
90
88
|
};
|
|
91
89
|
|
|
92
|
-
const
|
|
90
|
+
const resolveCurrentLevelCommands = async (
|
|
93
91
|
params: ResolveCommandsParams,
|
|
94
92
|
resolveChildCommands: ResolveChildCommands,
|
|
93
|
+
queryForCurrentLevel: string,
|
|
95
94
|
) => {
|
|
96
95
|
const { rootCommands, path, editor } = params;
|
|
97
96
|
let currentCommands = rootCommands;
|
|
98
97
|
let currentPath: CommandPathEntry[] = [];
|
|
99
98
|
|
|
100
|
-
for (
|
|
99
|
+
for (let index = 0; index < path.length; index++) {
|
|
100
|
+
const item = path[index];
|
|
101
|
+
const isLastPathItem = index === path.length - 1;
|
|
101
102
|
const command = currentCommands.find((entry) => entry.name === item.name);
|
|
102
103
|
if (!command?.getCommands) {
|
|
103
|
-
return
|
|
104
|
+
return {
|
|
105
|
+
commands: [],
|
|
106
|
+
path: currentPath,
|
|
107
|
+
};
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
currentPath = [...currentPath, toPathEntry(command)];
|
|
107
111
|
currentCommands = await resolveChildCommands({
|
|
108
112
|
command,
|
|
109
113
|
path: currentPath,
|
|
110
|
-
query: '',
|
|
114
|
+
query: isLastPathItem ? queryForCurrentLevel : '',
|
|
111
115
|
editor,
|
|
112
116
|
});
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
return
|
|
116
|
-
|
|
119
|
+
return {
|
|
120
|
+
commands: currentCommands,
|
|
121
|
+
path: currentPath,
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const resolveBrowseCommands = async (
|
|
126
|
+
params: ResolveCommandsParams,
|
|
127
|
+
resolveChildCommands: ResolveChildCommands,
|
|
128
|
+
) => {
|
|
129
|
+
const currentLevel = await resolveCurrentLevelCommands(
|
|
130
|
+
params,
|
|
131
|
+
resolveChildCommands,
|
|
132
|
+
'',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return currentLevel.commands.map((command) =>
|
|
136
|
+
toResolvedCommand(command, currentLevel.path),
|
|
117
137
|
);
|
|
118
138
|
};
|
|
119
139
|
|
|
@@ -121,38 +141,20 @@ const resolveSearchCommands = async (
|
|
|
121
141
|
params: ResolveCommandsParams,
|
|
122
142
|
resolveChildCommands: ResolveChildCommands,
|
|
123
143
|
) => {
|
|
124
|
-
const {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
): Promise<void> => {
|
|
131
|
-
for (const command of levelCommands) {
|
|
132
|
-
if (command.getCommands) {
|
|
133
|
-
if (isCommandMatchesQuery(command, filter)) {
|
|
134
|
-
commands.push(toResolvedCommand(command, parentPath));
|
|
135
|
-
}
|
|
144
|
+
const { filter } = params;
|
|
145
|
+
const currentLevel = await resolveCurrentLevelCommands(
|
|
146
|
+
params,
|
|
147
|
+
resolveChildCommands,
|
|
148
|
+
filter,
|
|
149
|
+
);
|
|
136
150
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
path: currentPath,
|
|
141
|
-
query: filter,
|
|
142
|
-
editor,
|
|
143
|
-
});
|
|
144
|
-
await walk(children, currentPath);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (command.action && isCommandMatchesQuery(command, filter)) {
|
|
149
|
-
commands.push(toResolvedCommand(command, parentPath));
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
};
|
|
151
|
+
const matchedCommands = currentLevel.commands.filter((command) =>
|
|
152
|
+
isCommandMatchesQuery(command, filter),
|
|
153
|
+
);
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
return matchedCommands.map((command) =>
|
|
156
|
+
toResolvedCommand(command, currentLevel.path),
|
|
157
|
+
);
|
|
156
158
|
};
|
|
157
159
|
|
|
158
160
|
export class CommandsResolver {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
background-color: var(--kona-editor-background-color, #fff);
|
|
12
12
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.025);
|
|
13
13
|
border: 1px solid var(--kona-editor-border-color, #ddd);
|
|
14
|
-
border-radius:
|
|
14
|
+
border-radius: 8px;
|
|
15
15
|
transition:
|
|
16
16
|
transform 0.12s ease,
|
|
17
17
|
opacity 0.12s ease;
|
|
@@ -79,29 +79,13 @@
|
|
|
79
79
|
display: flex;
|
|
80
80
|
flex: 1;
|
|
81
81
|
min-width: 0;
|
|
82
|
-
flex-direction:
|
|
83
|
-
row-gap: 2px;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.breadcrumb {
|
|
87
|
-
font-size: 11px;
|
|
88
|
-
color: var(--kona-editor-secondary-text-color, #777);
|
|
89
|
-
white-space: nowrap;
|
|
90
|
-
overflow: hidden;
|
|
91
|
-
text-overflow: ellipsis;
|
|
82
|
+
flex-direction: row;
|
|
92
83
|
}
|
|
93
84
|
|
|
94
85
|
.submenu {
|
|
95
86
|
color: var(--kona-editor-secondary-text-color, #777);
|
|
96
87
|
}
|
|
97
88
|
|
|
98
|
-
.path {
|
|
99
|
-
padding: 6px 8px;
|
|
100
|
-
font-size: 11px;
|
|
101
|
-
color: var(--kona-editor-secondary-text-color, #777);
|
|
102
|
-
border-bottom: 1px solid var(--kona-editor-border-color, #ddd);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
89
|
.systemRow {
|
|
106
90
|
min-height: 32px;
|
|
107
91
|
padding: 8px;
|
|
@@ -26,19 +26,42 @@ export const useResolvedCommands = (params: Params) => {
|
|
|
26
26
|
const { rootCommands, filter, path, editor, isOpen } = params;
|
|
27
27
|
const resolverRef = useRef(new CommandsResolver());
|
|
28
28
|
const [state, setState] = useState<ResolvedCommandsState>(EMPTY_STATE);
|
|
29
|
+
const prevIsOpenRef = useRef(false);
|
|
30
|
+
const prevPathKeyRef = useRef('');
|
|
31
|
+
const prevQueryRef = useRef('');
|
|
29
32
|
const query = typeof filter === 'string' ? filter : '';
|
|
30
33
|
|
|
31
34
|
useEffect(() => {
|
|
32
35
|
if (!isOpen || filter === false) {
|
|
33
36
|
setState(EMPTY_STATE);
|
|
37
|
+
prevIsOpenRef.current = false;
|
|
38
|
+
prevPathKeyRef.current = '';
|
|
39
|
+
prevQueryRef.current = '';
|
|
34
40
|
return;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const pathKey = path.map((item) => item.name).join('/');
|
|
44
|
+
const isNewSession = !prevIsOpenRef.current;
|
|
45
|
+
const isPathChanged = !isNewSession && prevPathKeyRef.current !== pathKey;
|
|
46
|
+
const isQueryChanged = !isNewSession && prevQueryRef.current !== query;
|
|
47
|
+
|
|
48
|
+
if (isNewSession || isPathChanged) {
|
|
49
|
+
setState({
|
|
50
|
+
commands: [],
|
|
51
|
+
isLoading: true,
|
|
52
|
+
isError: false,
|
|
53
|
+
});
|
|
54
|
+
} else if (isQueryChanged) {
|
|
55
|
+
setState((state) => ({
|
|
56
|
+
...state,
|
|
57
|
+
isLoading: true,
|
|
58
|
+
isError: false,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
prevIsOpenRef.current = true;
|
|
63
|
+
prevPathKeyRef.current = pathKey;
|
|
64
|
+
prevQueryRef.current = query;
|
|
42
65
|
|
|
43
66
|
const timeout = window.setTimeout(
|
|
44
67
|
() => {
|
|
@@ -49,8 +72,6 @@ export const useResolvedCommands = (params: Params) => {
|
|
|
49
72
|
editor,
|
|
50
73
|
});
|
|
51
74
|
|
|
52
|
-
setState(request.state);
|
|
53
|
-
|
|
54
75
|
request.promise.then((resolved) => {
|
|
55
76
|
setState(resolved);
|
|
56
77
|
});
|