@tooee/search 0.2.3 → 0.3.0
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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/nav-search-store.d.ts +21 -15
- package/dist/nav-search-store.d.ts.map +1 -1
- package/dist/nav-search-store.js +122 -105
- package/dist/nav-search-store.js.map +1 -1
- package/dist/{SearchBar.d.ts → search-bar.d.ts} +2 -2
- package/dist/search-bar.d.ts.map +1 -0
- package/dist/{SearchBar.js → search-bar.js} +12 -10
- package/dist/search-bar.js.map +1 -0
- package/dist/search-hook.d.ts +6 -6
- package/dist/search-hook.d.ts.map +1 -1
- package/dist/search-hook.js +55 -35
- package/dist/search-hook.js.map +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +6 -5
- package/dist/search.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +9 -9
- package/src/nav-search-store.ts +217 -180
- package/src/{SearchBar.tsx → search-bar.tsx} +20 -20
- package/src/search-hook.ts +111 -93
- package/src/search.ts +15 -13
- package/dist/SearchBar.d.ts.map +0 -1
- package/dist/SearchBar.js.map +0 -1
package/src/search-hook.ts
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from "react"
|
|
2
|
-
import { useSelector } from "@xstate/store-react"
|
|
3
|
-
import { useCommand, useMode, useSetMode
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useSelector } from "@xstate/store-react";
|
|
3
|
+
import { useCommand, useMode, useSetMode } from "@tooee/commands";
|
|
4
|
+
import type { Mode } from "@tooee/commands";
|
|
4
5
|
import {
|
|
5
6
|
createNavSearchStore,
|
|
6
7
|
selectCurrentMatchIndex,
|
|
7
8
|
selectMatches,
|
|
8
9
|
selectSearchActive,
|
|
9
10
|
selectSearchQuery,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type RowKey,
|
|
13
|
-
} from "./nav-search-store.js"
|
|
11
|
+
} from "./nav-search-store.js";
|
|
12
|
+
import type { NavSearchDeps, NavSearchStore, RowKey } from "./nav-search-store.js";
|
|
14
13
|
|
|
15
14
|
export interface UseSearchOptions {
|
|
16
|
-
match: (query: string) => number[]
|
|
17
|
-
onJump: (index: number) => void
|
|
18
|
-
enabled?: boolean
|
|
19
|
-
deps?: readonly unknown[]
|
|
15
|
+
match: (query: string) => number[];
|
|
16
|
+
onJump: (index: number) => void;
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
deps?: readonly unknown[];
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export interface SearchState {
|
|
23
|
-
searchQuery: string
|
|
24
|
-
searchActive: boolean
|
|
25
|
-
setSearchQuery: (query: string) => void
|
|
26
|
-
matchingLines: number[]
|
|
27
|
-
currentMatchIndex: number
|
|
28
|
-
submitSearch: () => void
|
|
22
|
+
searchQuery: string;
|
|
23
|
+
searchActive: boolean;
|
|
24
|
+
setSearchQuery: (query: string) => void;
|
|
25
|
+
matchingLines: number[];
|
|
26
|
+
currentMatchIndex: number;
|
|
27
|
+
submitSearch: () => void;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
const CURSOR_MODES: Mode[] = ["cursor"]
|
|
32
|
-
const ALL_MODES: Mode[] = ["cursor", "select", "insert"]
|
|
30
|
+
const CURSOR_MODES: Mode[] = ["cursor"];
|
|
31
|
+
const ALL_MODES: Mode[] = ["cursor", "select", "insert"];
|
|
33
32
|
|
|
34
|
-
export function useNavSearchStore(options: {
|
|
35
|
-
keys: readonly RowKey[]
|
|
36
|
-
isSelectable?: (index: number) => boolean
|
|
37
|
-
preserveCursorByKey?: boolean
|
|
33
|
+
export const useNavSearchStore = function useNavSearchStore(options: {
|
|
34
|
+
keys: readonly RowKey[];
|
|
35
|
+
isSelectable?: (index: number) => boolean;
|
|
36
|
+
preserveCursorByKey?: boolean;
|
|
38
37
|
}): NavSearchStore {
|
|
39
|
-
const depsRef = useRef<NavSearchDeps>({ isSelectable: options.isSelectable ?? (() => true) })
|
|
40
|
-
depsRef.current.isSelectable = options.isSelectable ?? (() => true)
|
|
41
|
-
const [store] = useState(() =>
|
|
42
|
-
createNavSearchStore({
|
|
43
|
-
)
|
|
38
|
+
const depsRef = useRef<NavSearchDeps>({ isSelectable: options.isSelectable ?? (() => true) });
|
|
39
|
+
depsRef.current.isSelectable = options.isSelectable ?? (() => true);
|
|
40
|
+
const [store, setStore] = useState(() =>
|
|
41
|
+
createNavSearchStore({ deps: depsRef.current, keys: options.keys }),
|
|
42
|
+
);
|
|
43
|
+
void setStore;
|
|
44
44
|
useEffect(() => {
|
|
45
|
-
const currentKeys = store.getSnapshot().context.rowKeys
|
|
45
|
+
const currentKeys = store.getSnapshot().context.rowKeys;
|
|
46
46
|
const keysChanged =
|
|
47
47
|
currentKeys.length !== options.keys.length ||
|
|
48
|
-
currentKeys.some((key, index) => !Object.is(key, options.keys[index]))
|
|
48
|
+
currentKeys.some((key, index) => !Object.is(key, options.keys[index]));
|
|
49
49
|
if (keysChanged) {
|
|
50
50
|
store.trigger.rowsChanged({
|
|
51
51
|
keys: options.keys,
|
|
52
52
|
preserveCursorByKey: options.preserveCursorByKey,
|
|
53
|
-
})
|
|
53
|
+
});
|
|
54
54
|
}
|
|
55
|
-
}, [store, options.keys, options.preserveCursorByKey])
|
|
56
|
-
return store
|
|
57
|
-
}
|
|
55
|
+
}, [store, options.keys, options.preserveCursorByKey]);
|
|
56
|
+
return store;
|
|
57
|
+
};
|
|
58
58
|
|
|
59
|
-
export function useSearchBindings(
|
|
59
|
+
export const useSearchBindings = function useSearchBindings(
|
|
60
60
|
store: NavSearchStore,
|
|
61
61
|
{
|
|
62
62
|
match,
|
|
@@ -65,101 +65,119 @@ export function useSearchBindings(
|
|
|
65
65
|
deps = [],
|
|
66
66
|
}: Omit<UseSearchOptions, "onJump"> & { onJump?: (index: number) => void },
|
|
67
67
|
): SearchState {
|
|
68
|
-
const mode = useMode()
|
|
69
|
-
const setMode = useSetMode()
|
|
70
|
-
const matchRef = useRef(match)
|
|
71
|
-
matchRef.current = match
|
|
72
|
-
const onJumpRef = useRef(onJump)
|
|
73
|
-
onJumpRef.current = onJump
|
|
68
|
+
const mode = useMode();
|
|
69
|
+
const setMode = useSetMode();
|
|
70
|
+
const matchRef = useRef(match);
|
|
71
|
+
matchRef.current = match;
|
|
72
|
+
const onJumpRef = useRef(onJump);
|
|
73
|
+
onJumpRef.current = onJump;
|
|
74
74
|
|
|
75
75
|
useEffect(() => {
|
|
76
|
-
const subscription = store.on("jumped", ({ index }) => onJumpRef.current?.(index))
|
|
77
|
-
return () =>
|
|
78
|
-
|
|
76
|
+
const subscription = store.on("jumped", ({ index }) => onJumpRef.current?.(index));
|
|
77
|
+
return () => {
|
|
78
|
+
subscription.unsubscribe();
|
|
79
|
+
};
|
|
80
|
+
}, [store]);
|
|
79
81
|
useEffect(() => {
|
|
80
|
-
const subscription = store.on("restoreMode", ({ mode: restored }) =>
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
const subscription = store.on("restoreMode", ({ mode: restored }) => {
|
|
83
|
+
setMode(restored);
|
|
84
|
+
});
|
|
85
|
+
return () => {
|
|
86
|
+
subscription.unsubscribe();
|
|
87
|
+
};
|
|
88
|
+
}, [store, setMode]);
|
|
83
89
|
|
|
84
|
-
const searchQuery = useSelector(store, (snapshot) => selectSearchQuery(snapshot.context))
|
|
85
|
-
const searchActive = useSelector(store, (snapshot) => selectSearchActive(snapshot.context))
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
const searchQuery = useSelector(store, (snapshot) => selectSearchQuery(snapshot.context));
|
|
91
|
+
const searchActive = useSelector(store, (snapshot) => selectSearchActive(snapshot.context));
|
|
92
|
+
const selectedMatches = useSelector(store, (snapshot) => selectMatches(snapshot.context));
|
|
93
|
+
// Deferred(lint-sweep): redesign the public selector result as readonly (separate stream)
|
|
94
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- preserve the existing mutable public SearchState type
|
|
95
|
+
const matchingLines = selectedMatches as number[];
|
|
89
96
|
const currentMatchIndex = useSelector(store, (snapshot) =>
|
|
90
97
|
selectCurrentMatchIndex(snapshot.context),
|
|
91
|
-
)
|
|
98
|
+
);
|
|
92
99
|
|
|
93
100
|
const updateSearchQuery = useCallback(
|
|
94
101
|
(query: string) => {
|
|
95
|
-
store.trigger.searchChanged({
|
|
102
|
+
store.trigger.searchChanged({ matches: query ? matchRef.current(query) : [], query });
|
|
96
103
|
},
|
|
97
104
|
[store],
|
|
98
|
-
)
|
|
105
|
+
);
|
|
99
106
|
|
|
100
107
|
// Re-match a committed query once when the caller-declared content changes.
|
|
101
|
-
const depsRef = useRef(deps)
|
|
108
|
+
const depsRef = useRef(deps);
|
|
102
109
|
useEffect(() => {
|
|
103
110
|
if (
|
|
104
111
|
depsRef.current.every((value, index) => Object.is(value, deps[index])) &&
|
|
105
112
|
depsRef.current.length === deps.length
|
|
106
|
-
)
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
depsRef.current = deps;
|
|
117
|
+
const query = selectSearchQuery(store.getSnapshot().context);
|
|
118
|
+
if (query) {
|
|
119
|
+
store.trigger.searchChanged({ matches: matchRef.current(query), query });
|
|
120
|
+
}
|
|
121
|
+
}, [deps, store]);
|
|
112
122
|
|
|
113
123
|
useCommand({
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
enabled,
|
|
125
|
+
handler: () => {
|
|
126
|
+
store.trigger.searchStarted({ mode });
|
|
127
|
+
setMode("insert");
|
|
128
|
+
},
|
|
116
129
|
hotkey: "/",
|
|
130
|
+
id: "cursor-search-start",
|
|
117
131
|
modes: CURSOR_MODES,
|
|
132
|
+
title: "Search",
|
|
133
|
+
});
|
|
134
|
+
useCommand({
|
|
118
135
|
enabled,
|
|
119
136
|
handler: () => {
|
|
120
|
-
store.trigger.
|
|
121
|
-
setMode("insert")
|
|
137
|
+
store.trigger.searchNext({});
|
|
122
138
|
},
|
|
123
|
-
})
|
|
124
|
-
useCommand({
|
|
125
|
-
id: "cursor-search-next",
|
|
126
|
-
title: "Next match",
|
|
127
139
|
hotkey: "n",
|
|
140
|
+
id: "cursor-search-next",
|
|
128
141
|
modes: CURSOR_MODES,
|
|
129
|
-
|
|
142
|
+
title: "Next match",
|
|
130
143
|
when: () => !selectSearchActive(store.getSnapshot().context),
|
|
131
|
-
|
|
132
|
-
})
|
|
144
|
+
});
|
|
133
145
|
useCommand({
|
|
134
|
-
|
|
135
|
-
|
|
146
|
+
enabled,
|
|
147
|
+
handler: () => {
|
|
148
|
+
store.trigger.searchPrevious({});
|
|
149
|
+
},
|
|
136
150
|
hotkey: "shift+n",
|
|
151
|
+
id: "cursor-search-prev",
|
|
137
152
|
modes: CURSOR_MODES,
|
|
138
|
-
|
|
153
|
+
title: "Previous match",
|
|
139
154
|
when: () => !selectSearchActive(store.getSnapshot().context),
|
|
140
|
-
|
|
141
|
-
})
|
|
155
|
+
});
|
|
142
156
|
useCommand({
|
|
143
|
-
|
|
144
|
-
|
|
157
|
+
enabled,
|
|
158
|
+
handler: () => {
|
|
159
|
+
store.trigger.searchCancelled({});
|
|
160
|
+
},
|
|
145
161
|
hotkey: "escape",
|
|
162
|
+
id: "search-cancel",
|
|
146
163
|
modes: ALL_MODES,
|
|
147
|
-
|
|
164
|
+
title: "Cancel search",
|
|
148
165
|
when: () => selectSearchActive(store.getSnapshot().context),
|
|
149
|
-
|
|
150
|
-
})
|
|
166
|
+
});
|
|
151
167
|
|
|
152
168
|
return {
|
|
153
|
-
|
|
169
|
+
currentMatchIndex,
|
|
170
|
+
matchingLines,
|
|
154
171
|
searchActive,
|
|
172
|
+
searchQuery,
|
|
155
173
|
setSearchQuery: updateSearchQuery,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
}
|
|
174
|
+
submitSearch: () => {
|
|
175
|
+
store.trigger.searchSubmitted({});
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
};
|
|
161
179
|
|
|
162
|
-
export function useSearch(options: UseSearchOptions): SearchState {
|
|
163
|
-
const store = useNavSearchStore({ keys: [] })
|
|
164
|
-
return useSearchBindings(store, options)
|
|
165
|
-
}
|
|
180
|
+
export const useSearch = function useSearch(options: UseSearchOptions): SearchState {
|
|
181
|
+
const store = useNavSearchStore({ keys: [] });
|
|
182
|
+
return useSearchBindings(store, options);
|
|
183
|
+
};
|
package/src/search.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
export function findMatchingLines(text: string, query: string): number[] {
|
|
2
|
-
if (!query)
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
export const findMatchingLines = function findMatchingLines(text: string, query: string): number[] {
|
|
2
|
+
if (!query) {
|
|
3
|
+
return [];
|
|
4
|
+
}
|
|
5
|
+
const lowerQuery = query.toLowerCase();
|
|
6
|
+
const results: number[] = [];
|
|
7
|
+
let lineStart = 0;
|
|
8
|
+
let lineNum = 0;
|
|
9
|
+
for (let i = 0; i <= text.length; i += 1) {
|
|
8
10
|
if (i === text.length || text[i] === "\n") {
|
|
9
|
-
const line = text.slice(lineStart, i).toLowerCase()
|
|
11
|
+
const line = text.slice(lineStart, i).toLowerCase();
|
|
10
12
|
if (line.includes(lowerQuery)) {
|
|
11
|
-
results.push(lineNum)
|
|
13
|
+
results.push(lineNum);
|
|
12
14
|
}
|
|
13
|
-
lineStart = i + 1
|
|
14
|
-
lineNum
|
|
15
|
+
lineStart = i + 1;
|
|
16
|
+
lineNum += 1;
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
|
-
return results
|
|
18
|
-
}
|
|
19
|
+
return results;
|
|
20
|
+
};
|
package/dist/SearchBar.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../src/SearchBar.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EAAE,SAAS,EACnB,UAAU,EACV,YAAY,GACb,EAAE,cAAc,6BAoChB"}
|
package/dist/SearchBar.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBar.js","sourceRoot":"","sources":["../src/SearchBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAWxC,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EAAE,SAAS,EACnB,UAAU,EACV,YAAY,GACG;IACf,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE5B,MAAM,YAAY,GAChB,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG,CAAC;QACxC,CAAC,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,UAAU,EAAE;QAC5C,CAAC,CAAC,UAAU,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YACpC,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,EAAE,CAAA;IAEV,OAAO,CACL,eACE,KAAK,EAAE;YACL,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,CAAC;YACb,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,CAAC;SAChB,aAED,eAAM,OAAO,EAAC,GAAG,EAAC,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,GAAI,EACjD,gBACE,KAAK,EAAE,KAAK,EACZ,OAAO,QACP,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,eAAe,EAAC,aAAa,EAC7B,sBAAsB,EAAC,aAAa,EACpC,SAAS,EAAE,KAAK,CAAC,IAAI,EACrB,WAAW,EAAE,KAAK,CAAC,MAAM,EACzB,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAC9C,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,GACtB,EACD,YAAY,CAAC,CAAC,CAAC,eAAM,OAAO,EAAE,IAAI,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,GAAI,CAAC,CAAC,CAAC,IAAI,IACxF,CACP,CAAA;AACH,CAAC"}
|