giggles 0.3.2 → 0.3.3
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/ui/index.d.ts +42 -1
- package/dist/ui/index.js +146 -0
- package/package.json +1 -1
package/dist/ui/index.d.ts
CHANGED
|
@@ -32,4 +32,45 @@ type TextInputProps = {
|
|
|
32
32
|
};
|
|
33
33
|
declare function TextInput({ label, value, onChange, onSubmit, placeholder, render }: TextInputProps): react_jsx_runtime.JSX.Element;
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
type SelectOption<T> = {
|
|
36
|
+
label: string;
|
|
37
|
+
value: T;
|
|
38
|
+
};
|
|
39
|
+
type SelectRenderProps<T> = {
|
|
40
|
+
option: SelectOption<T>;
|
|
41
|
+
focused: boolean;
|
|
42
|
+
highlighted: boolean;
|
|
43
|
+
selected: boolean;
|
|
44
|
+
};
|
|
45
|
+
type SelectProps<T> = {
|
|
46
|
+
options: SelectOption<T>[];
|
|
47
|
+
value: T;
|
|
48
|
+
onChange: (value: T) => void;
|
|
49
|
+
onSubmit?: (value: T) => void;
|
|
50
|
+
onHighlight?: (value: T) => void;
|
|
51
|
+
label?: string;
|
|
52
|
+
immediate?: boolean;
|
|
53
|
+
direction?: 'vertical' | 'horizontal';
|
|
54
|
+
render?: (props: SelectRenderProps<T>) => React__default.ReactNode;
|
|
55
|
+
};
|
|
56
|
+
declare function Select<T>({ options, value, onChange, onSubmit, onHighlight, label, immediate, direction, render }: SelectProps<T>): react_jsx_runtime.JSX.Element;
|
|
57
|
+
|
|
58
|
+
type MultiSelectRenderProps<T> = {
|
|
59
|
+
option: SelectOption<T>;
|
|
60
|
+
focused: boolean;
|
|
61
|
+
highlighted: boolean;
|
|
62
|
+
selected: boolean;
|
|
63
|
+
};
|
|
64
|
+
type MultiSelectProps<T> = {
|
|
65
|
+
options: SelectOption<T>[];
|
|
66
|
+
value: T[];
|
|
67
|
+
onChange: (value: T[]) => void;
|
|
68
|
+
onSubmit?: (value: T[]) => void;
|
|
69
|
+
onHighlight?: (value: T) => void;
|
|
70
|
+
label?: string;
|
|
71
|
+
direction?: 'vertical' | 'horizontal';
|
|
72
|
+
render?: (props: MultiSelectRenderProps<T>) => React__default.ReactNode;
|
|
73
|
+
};
|
|
74
|
+
declare function MultiSelect<T>({ options, value, onChange, onSubmit, onHighlight, label, direction, render }: MultiSelectProps<T>): react_jsx_runtime.JSX.Element;
|
|
75
|
+
|
|
76
|
+
export { CommandPalette, type CommandPaletteRenderProps, MultiSelect, type MultiSelectRenderProps, Select, type SelectOption, type SelectRenderProps, TextInput, type TextInputRenderProps };
|
package/dist/ui/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FocusTrap,
|
|
3
|
+
GigglesError,
|
|
3
4
|
useFocus,
|
|
4
5
|
useKeybindingRegistry,
|
|
5
6
|
useKeybindings
|
|
@@ -174,7 +175,152 @@ function TextInput({ label, value, onChange, onSubmit, placeholder, render }) {
|
|
|
174
175
|
/* @__PURE__ */ jsx2(Text2, { dimColor: isPlaceholder, children: displayValue })
|
|
175
176
|
] });
|
|
176
177
|
}
|
|
178
|
+
|
|
179
|
+
// src/ui/Select.tsx
|
|
180
|
+
import React3, { useState as useState2 } from "react";
|
|
181
|
+
import { Box as Box2, Text as Text3 } from "ink";
|
|
182
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
183
|
+
function Select({
|
|
184
|
+
options,
|
|
185
|
+
value,
|
|
186
|
+
onChange,
|
|
187
|
+
onSubmit,
|
|
188
|
+
onHighlight,
|
|
189
|
+
label,
|
|
190
|
+
immediate,
|
|
191
|
+
direction = "vertical",
|
|
192
|
+
render
|
|
193
|
+
}) {
|
|
194
|
+
const seen = /* @__PURE__ */ new Set();
|
|
195
|
+
for (const opt of options) {
|
|
196
|
+
const key = String(opt.value);
|
|
197
|
+
if (seen.has(key)) {
|
|
198
|
+
throw new GigglesError("Select options must have unique values");
|
|
199
|
+
}
|
|
200
|
+
seen.add(key);
|
|
201
|
+
}
|
|
202
|
+
const focus = useFocus();
|
|
203
|
+
const [highlightIndex, setHighlightIndex] = useState2(0);
|
|
204
|
+
const safeIndex = options.length === 0 ? -1 : Math.min(highlightIndex, options.length - 1);
|
|
205
|
+
if (safeIndex !== highlightIndex) {
|
|
206
|
+
setHighlightIndex(Math.max(0, safeIndex));
|
|
207
|
+
}
|
|
208
|
+
const moveHighlight = (delta) => {
|
|
209
|
+
if (options.length === 0) return;
|
|
210
|
+
const next2 = Math.max(0, Math.min(options.length - 1, safeIndex + delta));
|
|
211
|
+
if (next2 !== safeIndex) {
|
|
212
|
+
setHighlightIndex(next2);
|
|
213
|
+
onHighlight == null ? void 0 : onHighlight(options[next2].value);
|
|
214
|
+
if (immediate) {
|
|
215
|
+
onChange(options[next2].value);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const prev = () => moveHighlight(-1);
|
|
220
|
+
const next = () => moveHighlight(1);
|
|
221
|
+
const navBindings = direction === "vertical" ? { j: next, k: prev, down: next, up: prev } : { l: next, h: prev, right: next, left: prev };
|
|
222
|
+
useKeybindings(focus, {
|
|
223
|
+
...navBindings,
|
|
224
|
+
enter: () => {
|
|
225
|
+
if (options.length === 0) return;
|
|
226
|
+
if (immediate) {
|
|
227
|
+
onSubmit == null ? void 0 : onSubmit(options[safeIndex].value);
|
|
228
|
+
} else {
|
|
229
|
+
onChange(options[safeIndex].value);
|
|
230
|
+
onSubmit == null ? void 0 : onSubmit(options[safeIndex].value);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
const isHorizontal = direction === "horizontal";
|
|
235
|
+
return /* @__PURE__ */ jsxs3(Box2, { flexDirection: isHorizontal ? "row" : "column", gap: isHorizontal ? 1 : 0, children: [
|
|
236
|
+
label != null && /* @__PURE__ */ jsx3(Text3, { children: label }),
|
|
237
|
+
options.map((option, index) => {
|
|
238
|
+
const highlighted = index === safeIndex;
|
|
239
|
+
const selected = option.value === value;
|
|
240
|
+
if (render) {
|
|
241
|
+
return /* @__PURE__ */ jsx3(React3.Fragment, { children: render({ option, focused: focus.focused, highlighted, selected }) }, String(option.value));
|
|
242
|
+
}
|
|
243
|
+
return /* @__PURE__ */ jsxs3(Text3, { dimColor: !focus.focused, children: [
|
|
244
|
+
highlighted ? ">" : " ",
|
|
245
|
+
" ",
|
|
246
|
+
option.label
|
|
247
|
+
] }, String(option.value));
|
|
248
|
+
})
|
|
249
|
+
] });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/ui/MultiSelect.tsx
|
|
253
|
+
import React4, { useState as useState3 } from "react";
|
|
254
|
+
import { Box as Box3, Text as Text4 } from "ink";
|
|
255
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
256
|
+
function MultiSelect({
|
|
257
|
+
options,
|
|
258
|
+
value,
|
|
259
|
+
onChange,
|
|
260
|
+
onSubmit,
|
|
261
|
+
onHighlight,
|
|
262
|
+
label,
|
|
263
|
+
direction = "vertical",
|
|
264
|
+
render
|
|
265
|
+
}) {
|
|
266
|
+
const seen = /* @__PURE__ */ new Set();
|
|
267
|
+
for (const opt of options) {
|
|
268
|
+
const key = String(opt.value);
|
|
269
|
+
if (seen.has(key)) {
|
|
270
|
+
throw new GigglesError("MultiSelect options must have unique values");
|
|
271
|
+
}
|
|
272
|
+
seen.add(key);
|
|
273
|
+
}
|
|
274
|
+
const focus = useFocus();
|
|
275
|
+
const [highlightIndex, setHighlightIndex] = useState3(0);
|
|
276
|
+
const safeIndex = options.length === 0 ? -1 : Math.min(highlightIndex, options.length - 1);
|
|
277
|
+
if (safeIndex !== highlightIndex) {
|
|
278
|
+
setHighlightIndex(Math.max(0, safeIndex));
|
|
279
|
+
}
|
|
280
|
+
const moveHighlight = (delta) => {
|
|
281
|
+
if (options.length === 0) return;
|
|
282
|
+
const next2 = Math.max(0, Math.min(options.length - 1, safeIndex + delta));
|
|
283
|
+
if (next2 !== safeIndex) {
|
|
284
|
+
setHighlightIndex(next2);
|
|
285
|
+
onHighlight == null ? void 0 : onHighlight(options[next2].value);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
const toggle = () => {
|
|
289
|
+
if (options.length === 0) return;
|
|
290
|
+
const item = options[safeIndex].value;
|
|
291
|
+
const exists = value.includes(item);
|
|
292
|
+
onChange(exists ? value.filter((v) => v !== item) : [...value, item]);
|
|
293
|
+
};
|
|
294
|
+
const prev = () => moveHighlight(-1);
|
|
295
|
+
const next = () => moveHighlight(1);
|
|
296
|
+
const navBindings = direction === "vertical" ? { j: next, k: prev, down: next, up: prev } : { l: next, h: prev, right: next, left: prev };
|
|
297
|
+
useKeybindings(focus, {
|
|
298
|
+
...navBindings,
|
|
299
|
+
" ": toggle,
|
|
300
|
+
...onSubmit && { enter: () => onSubmit(value) }
|
|
301
|
+
});
|
|
302
|
+
const isHorizontal = direction === "horizontal";
|
|
303
|
+
return /* @__PURE__ */ jsxs4(Box3, { flexDirection: isHorizontal ? "row" : "column", gap: isHorizontal ? 1 : 0, children: [
|
|
304
|
+
label != null && /* @__PURE__ */ jsx4(Text4, { children: label }),
|
|
305
|
+
options.map((option, index) => {
|
|
306
|
+
const highlighted = index === safeIndex;
|
|
307
|
+
const selected = value.includes(option.value);
|
|
308
|
+
if (render) {
|
|
309
|
+
return /* @__PURE__ */ jsx4(React4.Fragment, { children: render({ option, focused: focus.focused, highlighted, selected }) }, String(option.value));
|
|
310
|
+
}
|
|
311
|
+
return /* @__PURE__ */ jsxs4(Text4, { dimColor: !focus.focused, children: [
|
|
312
|
+
highlighted ? ">" : " ",
|
|
313
|
+
" [",
|
|
314
|
+
selected ? "x" : " ",
|
|
315
|
+
"] ",
|
|
316
|
+
option.label
|
|
317
|
+
] }, String(option.value));
|
|
318
|
+
})
|
|
319
|
+
] });
|
|
320
|
+
}
|
|
177
321
|
export {
|
|
178
322
|
CommandPalette,
|
|
323
|
+
MultiSelect,
|
|
324
|
+
Select,
|
|
179
325
|
TextInput
|
|
180
326
|
};
|