@tooee/choose 0.7.3 → 0.9.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/choose-filter.d.ts +2 -11
- package/dist/choose-filter.d.ts.map +1 -1
- package/dist/choose-filter.js +1 -10
- package/dist/choose-filter.js.map +1 -1
- package/dist/choose-highlighted-text.d.ts +2 -8
- package/dist/choose-highlighted-text.d.ts.map +1 -1
- package/dist/choose-highlighted-text.js +1 -31
- package/dist/choose-highlighted-text.js.map +1 -1
- package/dist/choose-list.d.ts +2 -27
- package/dist/choose-list.d.ts.map +1 -1
- package/dist/choose-list.js +1 -56
- package/dist/choose-list.js.map +1 -1
- package/dist/choose-panel.d.ts +7 -9
- package/dist/choose-panel.d.ts.map +1 -1
- package/dist/choose-panel.js +5 -8
- package/dist/choose-panel.js.map +1 -1
- package/dist/choose-store.d.ts +3 -0
- package/dist/choose-store.d.ts.map +1 -0
- package/dist/choose-store.js +2 -0
- package/dist/choose-store.js.map +1 -0
- package/dist/choose.d.ts +3 -7
- package/dist/choose.d.ts.map +1 -1
- package/dist/choose.js +10 -12
- package/dist/choose.js.map +1 -1
- package/dist/choose.public-contract.typecheck.d.ts +11 -0
- package/dist/choose.public-contract.typecheck.d.ts.map +1 -0
- package/dist/choose.public-contract.typecheck.js +2 -0
- package/dist/choose.public-contract.typecheck.js.map +1 -0
- package/dist/fuzzy.d.ts +2 -8
- package/dist/fuzzy.d.ts.map +1 -1
- package/dist/fuzzy.js +1 -16
- package/dist/fuzzy.js.map +1 -1
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +2 -1
- package/dist/launch.js.map +1 -1
- package/dist/source.d.ts +1 -4
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +1 -13
- package/dist/source.js.map +1 -1
- package/dist/types.d.ts +1 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/use-choose-dialog.d.ts.map +1 -1
- package/dist/use-choose-dialog.js +22 -84
- package/dist/use-choose-dialog.js.map +1 -1
- package/dist/use-choose.d.ts +2 -79
- package/dist/use-choose.d.ts.map +1 -1
- package/dist/use-choose.js +1 -402
- package/dist/use-choose.js.map +1 -1
- package/package.json +8 -7
- package/src/choose-filter.tsx +2 -47
- package/src/choose-highlighted-text.tsx +2 -49
- package/src/choose-list.tsx +2 -148
- package/src/choose-panel.tsx +18 -71
- package/src/choose-store.ts +12 -0
- package/src/choose.public-contract.typecheck.ts +12 -0
- package/src/choose.tsx +20 -24
- package/src/fuzzy.ts +2 -28
- package/src/launch.tsx +8 -2
- package/src/source.ts +1 -18
- package/src/types.ts +7 -28
- package/src/use-choose-dialog.tsx +30 -98
- package/src/use-choose.ts +9 -526
package/src/choose-list.tsx
CHANGED
|
@@ -1,148 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type { ScrollBoxRenderable } from "@opentui/core";
|
|
4
|
-
import { useTheme } from "@tooee/themes";
|
|
5
|
-
import { ChooseHighlightedText } from "./choose-highlighted-text.js";
|
|
6
|
-
import type { FuzzyMatch } from "./fuzzy.js";
|
|
7
|
-
import type { ChooseItem } from "./types.js";
|
|
8
|
-
import type { UseChooseResult } from "./use-choose.js";
|
|
9
|
-
|
|
10
|
-
export interface ChooseItemRenderContext {
|
|
11
|
-
item: ChooseItem;
|
|
12
|
-
match: FuzzyMatch;
|
|
13
|
-
index: number;
|
|
14
|
-
originalIndex: number;
|
|
15
|
-
positions: number[];
|
|
16
|
-
isActive: boolean;
|
|
17
|
-
isSelected: boolean;
|
|
18
|
-
defaultContent: ReactNode;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface ChooseListProps {
|
|
22
|
-
choose: UseChooseResult;
|
|
23
|
-
/** Fullscreen defaults to activate; picker overlays default to submit. */
|
|
24
|
-
rowClick?: "activate" | "submit" | "none";
|
|
25
|
-
renderItem?: (context: ChooseItemRenderContext) => ReactNode;
|
|
26
|
-
loadingContent?: ReactNode;
|
|
27
|
-
errorContent?: (error: string) => ReactNode;
|
|
28
|
-
emptyContent?: ReactNode;
|
|
29
|
-
/** Mouse guard for visible rows covered by another surface. */
|
|
30
|
-
suspended?: boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const ThemedLine = function ThemedLine({
|
|
34
|
-
content,
|
|
35
|
-
color,
|
|
36
|
-
}: {
|
|
37
|
-
content: ReactNode;
|
|
38
|
-
color: string;
|
|
39
|
-
}): ReactNode {
|
|
40
|
-
return (
|
|
41
|
-
<box height={1} style={{ paddingLeft: 2 }}>
|
|
42
|
-
{typeof content === "string" ? <text content={content} fg={color} /> : content}
|
|
43
|
-
</box>
|
|
44
|
-
);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const defaultErrorContent = function defaultErrorContent(error: string): ReactNode {
|
|
48
|
-
return `Error: ${error}`;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/** Scrollable chooser rows plus loading/error/empty presentation. */
|
|
52
|
-
export const ChooseList = function ChooseList({
|
|
53
|
-
choose,
|
|
54
|
-
rowClick = "activate",
|
|
55
|
-
renderItem,
|
|
56
|
-
loadingContent = "Loading...",
|
|
57
|
-
errorContent = defaultErrorContent,
|
|
58
|
-
emptyContent,
|
|
59
|
-
suspended,
|
|
60
|
-
}: ChooseListProps): ReactNode {
|
|
61
|
-
const { theme } = useTheme();
|
|
62
|
-
const scrollRef = useRef<ScrollBoxRenderable>(null);
|
|
63
|
-
const { state, controller } = choose;
|
|
64
|
-
const interactionSuspended = choose.view.suspended || (suspended ?? false);
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
const activeMatch = state.matches[state.activeIndex];
|
|
68
|
-
if (scrollRef.current && activeMatch !== undefined) {
|
|
69
|
-
scrollRef.current.scrollChildIntoView(`choose-item-${activeMatch.originalIndex}`);
|
|
70
|
-
}
|
|
71
|
-
}, [state.activeIndex, state.matches]);
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<scrollbox ref={scrollRef} style={{ flexGrow: 1 }} focused={false}>
|
|
75
|
-
{state.loading && <ThemedLine content={loadingContent} color={theme.textMuted} />}
|
|
76
|
-
|
|
77
|
-
{!state.loading && state.error !== null && state.error !== "" && (
|
|
78
|
-
<ThemedLine content={errorContent(state.error)} color={theme.error} />
|
|
79
|
-
)}
|
|
80
|
-
|
|
81
|
-
{!state.loading &&
|
|
82
|
-
(state.error?.length ?? 0) === 0 &&
|
|
83
|
-
state.matches.length === 0 &&
|
|
84
|
-
emptyContent !== null &&
|
|
85
|
-
emptyContent !== undefined && <ThemedLine content={emptyContent} color={theme.textMuted} />}
|
|
86
|
-
|
|
87
|
-
{state.matches.map((match, index): ReactNode => {
|
|
88
|
-
const isActive = index === state.activeIndex;
|
|
89
|
-
const isSelected = state.selectedOriginalIndices.has(match.originalIndex);
|
|
90
|
-
const defaultContent = (
|
|
91
|
-
<text fg={isActive ? theme.primary : theme.text} style={{ flexGrow: 1 }}>
|
|
92
|
-
{state.multi && (
|
|
93
|
-
<span fg={isSelected ? theme.accent : theme.textMuted}>
|
|
94
|
-
{isSelected ? "✓ " : " "}
|
|
95
|
-
</span>
|
|
96
|
-
)}
|
|
97
|
-
{(match.item.icon?.length ?? 0) > 0 && (
|
|
98
|
-
<span fg={theme.textMuted}>{`${match.item.icon} `}</span>
|
|
99
|
-
)}
|
|
100
|
-
<ChooseHighlightedText
|
|
101
|
-
text={match.item.text}
|
|
102
|
-
positions={match.positions}
|
|
103
|
-
highlightColor={theme.warning}
|
|
104
|
-
/>
|
|
105
|
-
{(match.item.description?.length ?? 0) > 0 && (
|
|
106
|
-
<span fg={theme.textMuted}>{` ${match.item.description}`}</span>
|
|
107
|
-
)}
|
|
108
|
-
</text>
|
|
109
|
-
);
|
|
110
|
-
const context: ChooseItemRenderContext = {
|
|
111
|
-
defaultContent,
|
|
112
|
-
index,
|
|
113
|
-
isActive,
|
|
114
|
-
isSelected,
|
|
115
|
-
item: match.item,
|
|
116
|
-
match,
|
|
117
|
-
originalIndex: match.originalIndex,
|
|
118
|
-
positions: match.positions,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<box
|
|
123
|
-
key={match.originalIndex}
|
|
124
|
-
id={`choose-item-${match.originalIndex}`}
|
|
125
|
-
flexDirection="row"
|
|
126
|
-
backgroundColor={isActive ? theme.backgroundElement : undefined}
|
|
127
|
-
style={{ paddingLeft: 1, paddingRight: 1 }}
|
|
128
|
-
onMouseDown={(event) => {
|
|
129
|
-
if (interactionSuspended || event.button !== 0 || rowClick === "none") {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (rowClick === "submit") {
|
|
133
|
-
event.preventDefault();
|
|
134
|
-
event.stopPropagation();
|
|
135
|
-
controller.setActiveIndex(index);
|
|
136
|
-
controller.submit();
|
|
137
|
-
} else {
|
|
138
|
-
controller.setActiveIndex(index);
|
|
139
|
-
}
|
|
140
|
-
}}
|
|
141
|
-
>
|
|
142
|
-
{renderItem ? renderItem(context) : defaultContent}
|
|
143
|
-
</box>
|
|
144
|
-
);
|
|
145
|
-
})}
|
|
146
|
-
</scrollbox>
|
|
147
|
-
);
|
|
148
|
-
};
|
|
1
|
+
export { ChooseList } from "@tooee/renderers";
|
|
2
|
+
export type { ChooseItemRenderContext, ChooseListProps } from "@tooee/renderers";
|
package/src/choose-panel.tsx
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import type { MouseEvent } from "@opentui/core";
|
|
2
3
|
import { useMode } from "@tooee/commands";
|
|
3
4
|
import type { Mode } from "@tooee/commands";
|
|
4
|
-
import {
|
|
5
|
+
import { OverlayPanel } from "@tooee/layout";
|
|
6
|
+
import type { PanelInset, PanelInsetValue } from "@tooee/layout";
|
|
5
7
|
|
|
6
8
|
export const buildChooseHints = function buildChooseHints(
|
|
7
9
|
mode: Mode,
|
|
@@ -25,14 +27,8 @@ export const buildChooseHints = function buildChooseHints(
|
|
|
25
27
|
return options.extra ? [...base, ...options.extra] : base;
|
|
26
28
|
};
|
|
27
29
|
|
|
28
|
-
export type ChoosePanelInsetValue =
|
|
29
|
-
|
|
30
|
-
export interface ChoosePanelInset {
|
|
31
|
-
left?: ChoosePanelInsetValue;
|
|
32
|
-
right?: ChoosePanelInsetValue;
|
|
33
|
-
top?: ChoosePanelInsetValue;
|
|
34
|
-
bottom?: ChoosePanelInsetValue;
|
|
35
|
-
}
|
|
30
|
+
export type ChoosePanelInsetValue = PanelInsetValue;
|
|
31
|
+
export type ChoosePanelInset = PanelInset;
|
|
36
32
|
|
|
37
33
|
export interface ChoosePanelProps {
|
|
38
34
|
title?: ReactNode;
|
|
@@ -44,9 +40,10 @@ export interface ChoosePanelProps {
|
|
|
44
40
|
footer?: ReactNode;
|
|
45
41
|
onClose?: () => void;
|
|
46
42
|
inset?: ChoosePanelInset;
|
|
43
|
+
onMouseDown?: (event: MouseEvent) => void;
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
/**
|
|
46
|
+
/** Chooser-specific overlay panel with mode-aware default hints. */
|
|
50
47
|
export const ChoosePanel = function ChoosePanel({
|
|
51
48
|
title,
|
|
52
49
|
filter,
|
|
@@ -57,74 +54,24 @@ export const ChoosePanel = function ChoosePanel({
|
|
|
57
54
|
footer,
|
|
58
55
|
onClose,
|
|
59
56
|
inset,
|
|
57
|
+
onMouseDown,
|
|
60
58
|
}: ChoosePanelProps): ReactNode {
|
|
61
|
-
const { theme } = useTheme();
|
|
62
59
|
const mode = useMode();
|
|
63
60
|
const defaults = buildChooseHints(mode, { multi });
|
|
64
61
|
const hintContent = hints ? hints({ defaults, mode }) : defaults.join(" ");
|
|
65
62
|
|
|
66
63
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
borderColor={theme.borderActive}
|
|
64
|
+
<OverlayPanel
|
|
65
|
+
title={title}
|
|
66
|
+
prompt={filter}
|
|
67
|
+
hints={hintContent}
|
|
68
|
+
statusRight={statusRight}
|
|
69
|
+
footer={footer}
|
|
70
|
+
onClose={onClose}
|
|
71
|
+
inset={inset}
|
|
72
|
+
onMouseDown={onMouseDown}
|
|
77
73
|
>
|
|
78
|
-
{title !== null && title !== undefined && (
|
|
79
|
-
<box
|
|
80
|
-
flexDirection="row"
|
|
81
|
-
height={1}
|
|
82
|
-
paddingLeft={1}
|
|
83
|
-
paddingRight={1}
|
|
84
|
-
backgroundColor={theme.backgroundElement}
|
|
85
|
-
>
|
|
86
|
-
{typeof title === "string" ? (
|
|
87
|
-
<text content={title} fg={theme.accent} style={{ flexGrow: 1 }} />
|
|
88
|
-
) : (
|
|
89
|
-
<box flexDirection="row" style={{ flexGrow: 1 }}>
|
|
90
|
-
{title}
|
|
91
|
-
</box>
|
|
92
|
-
)}
|
|
93
|
-
{onClose && <CloseButton onClose={onClose} />}
|
|
94
|
-
</box>
|
|
95
|
-
)}
|
|
96
|
-
|
|
97
|
-
{filter}
|
|
98
74
|
{children}
|
|
99
|
-
|
|
100
|
-
{footer !== null && footer !== undefined && (
|
|
101
|
-
<box height={1} paddingLeft={1} paddingRight={1}>
|
|
102
|
-
{footer}
|
|
103
|
-
</box>
|
|
104
|
-
)}
|
|
105
|
-
|
|
106
|
-
<box
|
|
107
|
-
flexDirection="row"
|
|
108
|
-
height={1}
|
|
109
|
-
paddingLeft={1}
|
|
110
|
-
paddingRight={1}
|
|
111
|
-
backgroundColor={theme.backgroundElement}
|
|
112
|
-
>
|
|
113
|
-
<box flexDirection="row" style={{ flexGrow: 1 }}>
|
|
114
|
-
{typeof hintContent === "string" ? (
|
|
115
|
-
<text content={hintContent} fg={theme.textMuted} />
|
|
116
|
-
) : (
|
|
117
|
-
hintContent
|
|
118
|
-
)}
|
|
119
|
-
</box>
|
|
120
|
-
{statusRight !== null &&
|
|
121
|
-
statusRight !== undefined &&
|
|
122
|
-
(typeof statusRight === "string" ? (
|
|
123
|
-
<text content={statusRight} fg={theme.textMuted} />
|
|
124
|
-
) : (
|
|
125
|
-
statusRight
|
|
126
|
-
))}
|
|
127
|
-
</box>
|
|
128
|
-
</box>
|
|
75
|
+
</OverlayPanel>
|
|
129
76
|
);
|
|
130
77
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createChooseStore,
|
|
3
|
+
selectActiveIndex,
|
|
4
|
+
selectError,
|
|
5
|
+
selectFilterQuery,
|
|
6
|
+
selectItems,
|
|
7
|
+
selectLoading,
|
|
8
|
+
selectMatches,
|
|
9
|
+
selectReloadRevision,
|
|
10
|
+
selectSelectedOriginalIndices,
|
|
11
|
+
} from "@tooee/renderers";
|
|
12
|
+
export type { ChooseStore, ChooseStoreContext, ChooseStoreEvents } from "@tooee/renderers";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Compile-time checks for the Choose component's 0.9 public props. */
|
|
2
|
+
import type { ChooseProps } from "./choose.js";
|
|
3
|
+
|
|
4
|
+
type Assert<T extends true> = T;
|
|
5
|
+
type IsNever<T> = [T] extends [never] ? true : false;
|
|
6
|
+
type Has<TKeys, TName> = [Extract<TKeys, TName>] extends [never] ? false : true;
|
|
7
|
+
|
|
8
|
+
type Props = keyof ChooseProps;
|
|
9
|
+
type RetainedProps = Assert<Has<Props, "actions" | "multi" | "prompt" | "title">>;
|
|
10
|
+
type RemovedProps = Assert<IsNever<Extract<Props, "commands" | "options">>>;
|
|
11
|
+
|
|
12
|
+
export type ChoosePublicContractChecks = [RetainedProps, RemovedProps];
|
package/src/choose.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useImperativeHandle } from "react";
|
|
2
|
-
import type { Ref } from "react";
|
|
2
|
+
import type { ReactNode, Ref } from "react";
|
|
3
3
|
import { AppLayout } from "@tooee/layout";
|
|
4
4
|
import { useHasOverlay, useHasModalOverlay } from "@tooee/overlays";
|
|
5
5
|
import { useTheme } from "@tooee/themes";
|
|
6
6
|
import { useThemeCommands, useQuitCommand } from "@tooee/shell";
|
|
7
|
-
import {
|
|
7
|
+
import { useSurfaceInvoke } from "@tooee/commands";
|
|
8
8
|
import type { ActionDefinition } from "@tooee/commands";
|
|
9
9
|
import { ChooseFilter } from "./choose-filter.js";
|
|
10
10
|
import { ChooseList } from "./choose-list.js";
|
|
@@ -14,13 +14,9 @@ import type { ChooseContentProvider, ChooseOptions, ChooseResult } from "./types
|
|
|
14
14
|
import { useChoose } from "./use-choose.js";
|
|
15
15
|
import type { ChooseController } from "./use-choose.js";
|
|
16
16
|
|
|
17
|
-
export interface ChooseProps {
|
|
17
|
+
export interface ChooseProps extends ChooseOptions {
|
|
18
18
|
contentProvider: ChooseContentProvider;
|
|
19
|
-
options?: ChooseOptions;
|
|
20
|
-
/** Legacy name retained for source compatibility. */
|
|
21
19
|
actions?: ActionDefinition[];
|
|
22
|
-
/** Additive alias used by new chooser compositions. */
|
|
23
|
-
commands?: ActionDefinition[];
|
|
24
20
|
controllerRef?: Ref<ChooseController>;
|
|
25
21
|
renderItem?: ChooseListProps["renderItem"];
|
|
26
22
|
/**
|
|
@@ -38,18 +34,19 @@ export interface ChooseProps {
|
|
|
38
34
|
|
|
39
35
|
export const Choose = function Choose({
|
|
40
36
|
contentProvider,
|
|
41
|
-
options,
|
|
42
37
|
actions,
|
|
43
|
-
commands,
|
|
44
38
|
controllerRef,
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
emptyMessage,
|
|
40
|
+
multi = false,
|
|
47
41
|
onCancel,
|
|
48
|
-
|
|
42
|
+
onConfirm,
|
|
43
|
+
placeholder,
|
|
44
|
+
prompt,
|
|
45
|
+
renderItem,
|
|
46
|
+
title,
|
|
47
|
+
}: ChooseProps): ReactNode {
|
|
49
48
|
const { theme } = useTheme();
|
|
50
|
-
const { invoke } =
|
|
51
|
-
const effectiveCommands = commands ?? actions;
|
|
52
|
-
const multi = options?.multi ?? false;
|
|
49
|
+
const { invoke } = useSurfaceInvoke();
|
|
53
50
|
|
|
54
51
|
const { name: themeName } = useThemeCommands();
|
|
55
52
|
useQuitCommand({ onQuit: () => onCancel?.() });
|
|
@@ -58,13 +55,13 @@ export const Choose = function Choose({
|
|
|
58
55
|
const hasModalOverlay = useHasModalOverlay();
|
|
59
56
|
|
|
60
57
|
const choose = useChoose({
|
|
61
|
-
commands:
|
|
58
|
+
commands: actions,
|
|
62
59
|
multi,
|
|
63
60
|
onCancel,
|
|
64
61
|
onSubmit: (result) => {
|
|
65
62
|
// Standalone behaviour: a command named `submit` wins over `onConfirm`, so
|
|
66
63
|
// action-driven CLIs keep their own submit semantics.
|
|
67
|
-
if (
|
|
64
|
+
if (actions?.some((action) => action.id === "submit") === true) {
|
|
68
65
|
invoke("submit");
|
|
69
66
|
return;
|
|
70
67
|
}
|
|
@@ -98,10 +95,10 @@ export const Choose = function Choose({
|
|
|
98
95
|
|
|
99
96
|
const hints = buildChooseHints(choose.view.mode, { multi });
|
|
100
97
|
let titleBar: { title: string } | undefined;
|
|
101
|
-
if (
|
|
102
|
-
titleBar = { title
|
|
103
|
-
} else if (
|
|
104
|
-
titleBar = { title:
|
|
98
|
+
if (title !== undefined && title !== "") {
|
|
99
|
+
titleBar = { title };
|
|
100
|
+
} else if (prompt !== undefined && prompt !== "") {
|
|
101
|
+
titleBar = { title: prompt };
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
return (
|
|
@@ -125,15 +122,14 @@ export const Choose = function Choose({
|
|
|
125
122
|
{ label: "", value: hints.join(" ") },
|
|
126
123
|
],
|
|
127
124
|
}}
|
|
128
|
-
scrollProps={{ focused: false }}
|
|
129
125
|
>
|
|
130
126
|
<box flexDirection="column" style={{ flexGrow: 1 }}>
|
|
131
|
-
<ChooseFilter choose={choose} placeholder={
|
|
127
|
+
<ChooseFilter choose={choose} placeholder={placeholder} />
|
|
132
128
|
<ChooseList
|
|
133
129
|
choose={choose}
|
|
134
130
|
rowClick="activate"
|
|
135
131
|
renderItem={renderItem}
|
|
136
|
-
emptyContent={
|
|
132
|
+
emptyContent={emptyMessage}
|
|
137
133
|
suspended={hasModalOverlay}
|
|
138
134
|
/>
|
|
139
135
|
</box>
|
package/src/fuzzy.ts
CHANGED
|
@@ -1,28 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export interface FuzzyMatch {
|
|
5
|
-
item: ChooseItem;
|
|
6
|
-
originalIndex: number;
|
|
7
|
-
score: number;
|
|
8
|
-
positions: number[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const fuzzyFilter = function fuzzyFilter(items: ChooseItem[], query: string): FuzzyMatch[] {
|
|
12
|
-
if (!query) {
|
|
13
|
-
return items.map((item, i) => ({ item, originalIndex: i, positions: [], score: 0 }));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const results: FuzzyMatch[] = [];
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < items.length; i += 1) {
|
|
19
|
-
const item = items[i];
|
|
20
|
-
const match = fuzzyMatchPositions(query, item.text);
|
|
21
|
-
if (match) {
|
|
22
|
-
results.push({ item, originalIndex: i, positions: match.positions, score: match.score });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
results.sort((a, b) => b.score - a.score);
|
|
27
|
-
return results;
|
|
28
|
-
};
|
|
1
|
+
export { fuzzyFilter } from "@tooee/renderers";
|
|
2
|
+
export type { FuzzyMatch } from "@tooee/renderers";
|
package/src/launch.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { runCliSession } from "@tooee/shell";
|
|
|
2
2
|
import type { ActionDefinition } from "@tooee/commands";
|
|
3
3
|
import { Choose } from "./choose.js";
|
|
4
4
|
import type { ChooseContentProvider, ChooseOptions, ChooseResult } from "./types.js";
|
|
5
|
+
import type { ReactNode } from "react";
|
|
5
6
|
|
|
6
7
|
export interface ChooseLaunchOptions {
|
|
7
8
|
contentProvider: ChooseContentProvider;
|
|
@@ -13,10 +14,14 @@ export const launch = async function launch(
|
|
|
13
14
|
opts: ChooseLaunchOptions,
|
|
14
15
|
): Promise<ChooseResult | null> {
|
|
15
16
|
const result = await runCliSession<ChooseResult>(
|
|
16
|
-
({ resolve, cancel }):
|
|
17
|
+
({ resolve, cancel }): ReactNode => (
|
|
17
18
|
<Choose
|
|
18
19
|
contentProvider={opts.contentProvider}
|
|
19
|
-
|
|
20
|
+
title={opts.options?.title}
|
|
21
|
+
prompt={opts.options?.prompt}
|
|
22
|
+
placeholder={opts.options?.placeholder}
|
|
23
|
+
multi={opts.options?.multi}
|
|
24
|
+
emptyMessage={opts.options?.emptyMessage}
|
|
20
25
|
actions={opts.actions}
|
|
21
26
|
onConfirm={resolve}
|
|
22
27
|
onCancel={cancel}
|
|
@@ -25,6 +30,7 @@ export const launch = async function launch(
|
|
|
25
30
|
{
|
|
26
31
|
provider: { initialMode: "insert" },
|
|
27
32
|
stdinPolicy: "tty-if-piped",
|
|
33
|
+
stdoutPolicy: "tty-if-redirected",
|
|
28
34
|
},
|
|
29
35
|
);
|
|
30
36
|
return result;
|
package/src/source.ts
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/** Resolve every public Choose source shape through one compatibility boundary. */
|
|
4
|
-
export const loadChooseSource = function loadChooseSource(
|
|
5
|
-
source: ChooseSource,
|
|
6
|
-
): ChooseItem[] | Promise<ChooseItem[]> {
|
|
7
|
-
if (Array.isArray(source)) {
|
|
8
|
-
return source;
|
|
9
|
-
}
|
|
10
|
-
if (typeof source === "function") {
|
|
11
|
-
return source();
|
|
12
|
-
}
|
|
13
|
-
return source.load();
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const chooseSourceError = function chooseSourceError(error: unknown): string {
|
|
17
|
-
return error instanceof Error ? error.message : String(error);
|
|
18
|
-
};
|
|
1
|
+
export { loadChooseSource } from "@tooee/renderers";
|
package/src/types.ts
CHANGED
|
@@ -1,28 +1,7 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export interface ChooseContentProvider {
|
|
9
|
-
load: () => Promise<ChooseItem[]> | ChooseItem[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** Any item source accepted by the shared chooser core. */
|
|
13
|
-
export type ChooseSource =
|
|
14
|
-
| ChooseItem[]
|
|
15
|
-
| ChooseContentProvider
|
|
16
|
-
| (() => ChooseItem[] | Promise<ChooseItem[]>);
|
|
17
|
-
|
|
18
|
-
export interface ChooseResult {
|
|
19
|
-
items: ChooseItem[];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface ChooseOptions {
|
|
23
|
-
multi?: boolean;
|
|
24
|
-
title?: string;
|
|
25
|
-
prompt?: string;
|
|
26
|
-
placeholder?: string;
|
|
27
|
-
emptyMessage?: string;
|
|
28
|
-
}
|
|
1
|
+
export type {
|
|
2
|
+
ChooseContentProvider,
|
|
3
|
+
ChooseItem,
|
|
4
|
+
ChooseOptions,
|
|
5
|
+
ChooseResult,
|
|
6
|
+
ChooseSource,
|
|
7
|
+
} from "@tooee/renderers";
|