Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/giant-doors-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clack/prompts": patch
---

docs: add jsdoc for `box`, `group`, and `group-multi-select`
61 changes: 61 additions & 0 deletions packages/prompts/src/box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import {
S_CORNER_TOP_RIGHT,
} from './common.js';

/**
* Alignment for content or titles within the box.
*/
export type BoxAlignment = 'left' | 'center' | 'right';

type BoxSymbols = [topLeft: string, topRight: string, bottomLeft: string, bottomRight: string];
Expand All @@ -28,13 +31,49 @@ const roundedSymbols: BoxSymbols = [
];
const squareSymbols: BoxSymbols = [S_BAR_START, S_BAR_START_RIGHT, S_BAR_END, S_BAR_END_RIGHT];

/**
* Options for the {@link box} prompt.
*/
export interface BoxOptions extends CommonOptions {
/**
* Alignment of the content (`'left'`, `'center'`, or `'right'`).
* @default 'left'
*/
contentAlign?: BoxAlignment;

/**
* Alignment of the title (`'left'`, `'center'`, or `'right'`).
* @default 'left'
*/
titleAlign?: BoxAlignment;

/**
* The width of the box, either `'auto'` to fit the content or a number for a fixed width.
* @default 'auto'
*/
width?: number | 'auto';

/**
* Padding around the title.
* @default 1
*/
titlePadding?: number;

/**
* Padding around the content.
* @default 2
*/
contentPadding?: number;

/**
* Use rounded corners when `true`, square corners when `false`.
* @default true
*/
rounded?: boolean;

/**
* Custom function to style the border characters.
*/
formatBorder?: (text: string) => string;
}

Expand All @@ -59,6 +98,28 @@ function getPaddingForLine(

const defaultFormatBorder = (text: string) => text;

/**
* Renders a customizable box around text content. It's similar to {@link note} but offers
* more styling options.
*
* @see https://bomb.sh/docs/clack/packages/prompts/#box
*
* @param message - The content to display inside the box.
* @param title - The title to display in the top border of the box.
* @param opts - Optional configuration for the box styling and behavior.
*
* @example
* ```ts
* import { box } from '@clack/prompts';
*
* box('This is the content of the box', 'Box Title', {
* contentAlign: 'center',
* titleAlign: 'center',
* width: 'auto',
* rounded: true,
* });
* ```
*/
export const box = (message = '', title = '', opts?: BoxOptions) => {
const output: Writable = opts?.output ?? process.stdout;
const columns = getColumns(output);
Expand Down
65 changes: 65 additions & 0 deletions packages/prompts/src/group-multi-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,81 @@ import {
import { limitOptions } from './limit-options.js';
import type { Option } from './select.js';

/**
* Options for the {@link groupMultiselect} prompt.
*/
export interface GroupMultiSelectOptions<Value> extends CommonOptions {
/**
* The message or question shown to the user above the input.
*/
message: string;

/**
* Grouped options to display. Each key is a group label, and each value is an array of options.
*/
options: Record<string, Option<Value>[]>;

/**
* The initially selected option(s).
*/
initialValues?: Value[];

/**
* The maximum number of items/options to display at once.
*/
maxItems?: number;

/**
* When `true` at least one option must be selected.
* @default true
*/
required?: boolean;

/**
* The value the cursor should be positioned at initially.
*/
cursorAt?: Value;

/**
* Whether entire groups can be selected at once.
* @default true
*/
selectableGroups?: boolean;

/**
* Number of blank lines between groups.
* @default 0
*/
groupSpacing?: number;
}

/**
* The `groupMultiselect` prompt extends the {@link multiselect} prompt to allow
* arranging distinct Multi-Selects, whilst keeping all of them interactive.
*
* @see https://bomb.sh/docs/clack/packages/prompts/#group-multiselect
*
* @example
* ```ts
* import { groupMultiselect } from '@clack/prompts';
*
* const result = await groupMultiselect({
* message: 'Define your project',
* options: {
* 'Testing': [
* { value: 'Jest', hint: 'JavaScript testing framework' },
* { value: 'Playwright', hint: 'End-to-end testing' },
* ],
* 'Language': [
* { value: 'js', label: 'JavaScript', hint: 'Dynamic typing' },
* { value: 'ts', label: 'TypeScript', hint: 'Static typing' },
* ],
* },
* });
* ```
*
* @param opts The options for the group multiselect prompt
*/
export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) => {
const { selectableGroups = true, groupSpacing = 0 } = opts;
const opt = (
Expand Down
39 changes: 35 additions & 4 deletions packages/prompts/src/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,58 @@ type Prettify<T> = {
[P in keyof T]: T[P];
} & {};

/**
* The return type of a {@link PromptGroup}.
* Resolves all prompt results, excluding the cancel symbol.
*/
export type PromptGroupAwaitedReturn<T> = {
[P in keyof T]: Exclude<Awaited<T[P]>, symbol>;
};

/**
* Options for the {@link group} utility.
*/
export interface PromptGroupOptions<T> {
/**
* Control how the group can be canceled
* if one of the prompts is canceled.
* Called when any one of the prompts is canceled.
*/
onCancel?: (opts: { results: Prettify<Partial<PromptGroupAwaitedReturn<T>>> }) => void;
}

/**
* A group of prompts to be displayed sequentially, with each prompt receiving
* the results of all previous prompts in the group.
*/
export type PromptGroup<T> = {
[P in keyof T]: (opts: {
results: Prettify<Partial<PromptGroupAwaitedReturn<Omit<T, P>>>>;
}) => undefined | Promise<T[P] | undefined>;
};

/**
* Define a group of prompts to be displayed
* and return a results of objects within the group
* The `group` utility provides a consistent way to combine a series of prompts,
* combining each answer into one object. Each prompt receives the results of
* all previously completed prompts, and are displayed sequentially.
*
* @see https://bomb.sh/docs/clack/packages/prompts/#group
*
* @example
* ```ts
* import { group, text, password } from '@clack/prompts';
*
* const account = await group({
* email: () => text({
* message: 'What is your email address?',
* }),
* username: ({ results }) => text({
* message: 'What is your username?',
* placeholder: results.email?.replace(/@.+$/, '').toLowerCase() ?? '',
* }),
* password: () => password({
* message: 'Define your password',
* }),
* });
* ```
*/
export const group = async <T>(
prompts: PromptGroup<T>,
Expand Down
Loading