Skip to content
Closed
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useTheme } from "@tui/context/theme"
import { DialogPrompt } from "@tui/ui/dialog-prompt"

interface DialogQuestionCommentProps {
question: string
value?: string
onSave: (comment: string) => void
onCancel: () => void
}

export function DialogQuestionComment(props: DialogQuestionCommentProps) {
const { theme } = useTheme()

return (
<DialogPrompt
title={props.question}
description={() => <text fg={theme.textMuted}>Add comment</text>}
placeholder="Optional comment..."
value={props.value}
onConfirm={(value) => props.onSave(value)}
onCancel={() => props.onCancel()}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { createMemo, createSignal, onMount } from "solid-js"
import { useTheme } from "@tui/context/theme"
import { useDialog } from "@tui/ui/dialog"
import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
import { DialogQuestionComment } from "./dialog-question-comment"
import { truncate } from "./helpers"
import type { SingleQuestionProps } from "./types"

export function DialogQuestionConfirm(props: SingleQuestionProps) {
const dialog = useDialog()
const { theme } = useTheme()
const [comment, setComment] = createSignal<string | undefined>(props.currentAnswer?.comment)

// Find current selection value
const currentValue = createMemo(() => props.currentAnswer?.value)

onMount(() => {
dialog.setSize("medium")
})

function openComment() {
dialog.push(() => (
<DialogQuestionComment
question={props.item.question}
value={comment()}
onSave={(c) => {
const trimmedComment = c.trim() || undefined
setComment(trimmedComment)
// Update just the comment, merging with existing answer
props.onAnswer({ comment: trimmedComment })
dialog.pop()
}}
onCancel={() => dialog.pop()}
/>
))
}

function confirmSelection(value: boolean) {
props.onAnswer({ value })
dialog.pop()
}

// Yes/No options
const options = createMemo<DialogSelectOption<boolean>[]>(() => [
{
title: "Yes",
value: true,
},
{
title: "No",
value: false,
},
])

return (
<DialogSelect
title={props.item.question}
options={options()}
current={typeof currentValue() === "boolean" ? currentValue() : undefined}
hideSearch={true}
onSelect={(option) => {
if (typeof option.value === "boolean") {
confirmSelection(option.value)
}
}}
beforeFooter={
comment() ? (
<box paddingLeft={4} paddingRight={4}>
<text fg={theme.textMuted}>💬 "{truncate(comment()!, 40)}"</text>
</box>
) : undefined
}
keybind={[
{
keybind: { name: "c", ctrl: false, meta: false, shift: false, super: false, leader: false },
title: "add comment",
onTrigger: () => openComment(),
},
]}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { createMemo, createSignal, onMount } from "solid-js"
import { useTheme } from "@tui/context/theme"
import { useDialog } from "@tui/ui/dialog"
import { DialogMultiSelect, type DialogMultiSelectOption } from "@tui/ui/dialog-multiselect"
import { DialogQuestionComment } from "./dialog-question-comment"
import { truncate } from "./helpers"
import type { SingleQuestionProps } from "./types"

export function DialogQuestionMultiSelect(props: SingleQuestionProps) {
const dialog = useDialog()
const { theme } = useTheme()
const [comment, setComment] = createSignal<string | undefined>(props.currentAnswer?.comment)

// Sort options with recommended first
const sortedOptions = createMemo(() => {
const opts = props.item.options ?? []
return [...opts].sort((a, b) => (b.recommended ? 1 : 0) - (a.recommended ? 1 : 0))
})

onMount(() => {
dialog.setSize("medium")
})

function openComment() {
dialog.push(() => (
<DialogQuestionComment
question={props.item.question}
value={comment()}
onSave={(c) => {
const trimmedComment = c.trim() || undefined
setComment(trimmedComment)
// Update just the comment, merging with existing answer
props.onAnswer({ comment: trimmedComment })
dialog.pop()
}}
onCancel={() => dialog.pop()}
/>
))
}

// Convert options to DialogMultiSelectOption format
const selectOptions = createMemo<DialogMultiSelectOption<string>[]>(() =>
sortedOptions().map((option) => ({
title: option.label,
value: option.value,
footer: option.recommended ? "(Recommended)" : undefined,
})),
)

// Get current values as array
const currentValues = createMemo(() => {
if (Array.isArray(props.currentAnswer?.value)) {
return props.currentAnswer.value
}
return []
})

function confirmSelection(selected: string[]) {
props.onAnswer({ value: selected })
dialog.pop()
}

return (
<DialogMultiSelect
title={props.item.question}
options={selectOptions()}
current={currentValues()}
hideSearch={true}
onSelect={confirmSelection}
beforeFooter={
comment() ? (
<box paddingLeft={4} paddingRight={4}>
<text fg={theme.textMuted}>💬 "{truncate(comment()!, 40)}"</text>
</box>
) : undefined
}
keybind={[
{
keybind: { name: "c", ctrl: false, meta: false, shift: false, super: false, leader: false },
title: "add comment",
onTrigger: () => openComment(),
},
]}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { createMemo, createSignal, onMount } from "solid-js"
import { useTheme } from "@tui/context/theme"
import { useDialog } from "@tui/ui/dialog"
import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
import { DialogQuestionComment } from "./dialog-question-comment"
import { truncate } from "./helpers"
import type { SingleQuestionProps } from "./types"

export function DialogQuestionSelect(props: SingleQuestionProps) {
const dialog = useDialog()
const { theme } = useTheme()
const [comment, setComment] = createSignal<string | undefined>(props.currentAnswer?.comment)

// Sort options with recommended first
const sortedOptions = createMemo(() => {
const opts = props.item.options ?? []
return [...opts].sort((a, b) => (b.recommended ? 1 : 0) - (a.recommended ? 1 : 0))
})

// Find current selection value
const currentValue = createMemo(() => props.currentAnswer?.value)

onMount(() => {
dialog.setSize("medium")
})

function openComment() {
dialog.push(() => (
<DialogQuestionComment
question={props.item.question}
value={comment()}
onSave={(c) => {
const trimmedComment = c.trim() || undefined
setComment(trimmedComment)
// Update just the comment, merging with existing answer
props.onAnswer({ comment: trimmedComment })
dialog.pop()
}}
onCancel={() => dialog.pop()}
/>
))
}

function confirmSelection(option: { value: string; label: string; recommended?: boolean }) {
props.onAnswer({ value: option.value })
dialog.pop()
}

// Convert options to DialogSelectOption format
const selectOptions = createMemo<DialogSelectOption<{ value: string; label: string; recommended?: boolean }>[]>(() =>
sortedOptions().map((option) => ({
title: option.label,
value: option,
footer: option.recommended ? "(Recommended)" : undefined,
})),
)

return (
<DialogSelect
title={props.item.question}
options={selectOptions()}
current={sortedOptions().find((o) => o.value === currentValue())}
hideSearch={true}
onSelect={(option) => confirmSelection(option.value)}
beforeFooter={
comment() ? (
<box paddingLeft={4} paddingRight={4}>
<text fg={theme.textMuted}>💬 "{truncate(comment()!, 40)}"</text>
</box>
) : undefined
}
keybind={[
{
keybind: { name: "c", ctrl: false, meta: false, shift: false, super: false, leader: false },
title: "add comment",
onTrigger: () => openComment(),
},
]}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useDialog } from "@tui/ui/dialog"
import { DialogPrompt } from "@tui/ui/dialog-prompt"
import type { SingleQuestionProps } from "./types"

export function DialogQuestionText(props: SingleQuestionProps) {
const dialog = useDialog()
const placeholder = typeof props.item.default === "string" ? props.item.default : "Enter your response..."
const initialValue = typeof props.currentAnswer?.value === "string" ? props.currentAnswer.value : ""

function confirmText(value: string) {
const trimmedValue = value.trim()
props.onAnswer({ value: trimmedValue || null })
dialog.pop()
}

return (
<DialogPrompt
title={props.item.question}
placeholder={placeholder}
value={initialValue}
onConfirm={confirmText}
onCancel={() => props.onCancel()}
/>
)
}
Loading