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.
@@ -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
- export { CommandPalette, type CommandPaletteRenderProps, TextInput, type TextInputRenderProps };
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "giggles",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",