Apply PR #15697: tweak(ui): make questions popup collapsible
commit
4133f55458
|
|
@ -4,6 +4,7 @@ import { useMutation } from "@tanstack/solid-query"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { DockPrompt } from "@opencode-ai/ui/dock-prompt"
|
import { DockPrompt } from "@opencode-ai/ui/dock-prompt"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2"
|
import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
|
|
@ -73,6 +74,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||||
customOn: cached?.customOn ?? ([] as boolean[]),
|
customOn: cached?.customOn ?? ([] as boolean[]),
|
||||||
editing: false,
|
editing: false,
|
||||||
focus: 0,
|
focus: 0,
|
||||||
|
collapsed: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
let root: HTMLDivElement | undefined
|
let root: HTMLDivElement | undefined
|
||||||
|
|
@ -87,6 +89,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||||
const on = createMemo(() => store.customOn[store.tab] === true)
|
const on = createMemo(() => store.customOn[store.tab] === true)
|
||||||
const multi = createMemo(() => question()?.multiple === true)
|
const multi = createMemo(() => question()?.multiple === true)
|
||||||
const count = createMemo(() => options().length + 1)
|
const count = createMemo(() => options().length + 1)
|
||||||
|
const num = createMemo(() => store.answers[store.tab]?.length ?? 0)
|
||||||
|
|
||||||
const summary = createMemo(() => {
|
const summary = createMemo(() => {
|
||||||
const n = Math.min(store.tab + 1, total())
|
const n = Math.min(store.tab + 1, total())
|
||||||
|
|
@ -98,6 +101,8 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||||
|
|
||||||
const last = createMemo(() => store.tab >= total() - 1)
|
const last = createMemo(() => store.tab >= total() - 1)
|
||||||
|
|
||||||
|
const fold = () => setStore("collapsed", (value) => !value)
|
||||||
|
|
||||||
const customUpdate = (value: string, selected: boolean = on()) => {
|
const customUpdate = (value: string, selected: boolean = on()) => {
|
||||||
const prev = input().trim()
|
const prev = input().trim()
|
||||||
const next = value.trim()
|
const next = value.trim()
|
||||||
|
|
@ -426,9 +431,21 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||||
ref={(el) => (root = el)}
|
ref={(el) => (root = el)}
|
||||||
onKeyDown={nav}
|
onKeyDown={nav}
|
||||||
header={
|
header={
|
||||||
<>
|
<div
|
||||||
|
data-action="session-question-toggle"
|
||||||
|
class="flex flex-1 min-w-0 items-center gap-2 cursor-default select-none"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
style={{ margin: "0 -10px", padding: "0 0 0 10px" }}
|
||||||
|
onClick={fold}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key !== "Enter" && event.key !== " ") return
|
||||||
|
event.preventDefault()
|
||||||
|
fold()
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div data-slot="question-header-title">{summary()}</div>
|
<div data-slot="question-header-title">{summary()}</div>
|
||||||
<div data-slot="question-progress">
|
<div data-slot="question-progress" class="ml-auto mr-1">
|
||||||
<For each={questions()}>
|
<For each={questions()}>
|
||||||
{(_, i) => (
|
{(_, i) => (
|
||||||
<button
|
<button
|
||||||
|
|
@ -437,13 +454,38 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||||
data-active={i() === store.tab}
|
data-active={i() === store.tab}
|
||||||
data-answered={answered(i())}
|
data-answered={answered(i())}
|
||||||
disabled={sending()}
|
disabled={sending()}
|
||||||
onClick={() => jump(i())}
|
onMouseDown={(event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
jump(i())
|
||||||
|
}}
|
||||||
aria-label={`${language.t("ui.tool.questions")} ${i() + 1}`}
|
aria-label={`${language.t("ui.tool.questions")} ${i() + 1}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</>
|
<div>
|
||||||
|
<IconButton
|
||||||
|
data-action="session-question-toggle-button"
|
||||||
|
icon="chevron-down"
|
||||||
|
size="normal"
|
||||||
|
variant="ghost"
|
||||||
|
classList={{ "rotate-180": store.collapsed }}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
fold()
|
||||||
|
}}
|
||||||
|
aria-label={store.collapsed ? language.t("session.todo.expand") : language.t("session.todo.collapse")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
footer={
|
footer={
|
||||||
<>
|
<>
|
||||||
|
|
@ -469,7 +511,30 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div data-slot="question-text">{question()?.question}</div>
|
<div
|
||||||
|
data-slot="question-text"
|
||||||
|
class="cursor-default"
|
||||||
|
classList={{
|
||||||
|
"mb-6": store.collapsed && num() === 0,
|
||||||
|
}}
|
||||||
|
role={store.collapsed ? "button" : undefined}
|
||||||
|
tabIndex={store.collapsed ? 0 : undefined}
|
||||||
|
onClick={fold}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (!store.collapsed) return
|
||||||
|
if (event.key !== "Enter" && event.key !== " ") return
|
||||||
|
event.preventDefault()
|
||||||
|
fold()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{question()?.question}
|
||||||
|
</div>
|
||||||
|
<Show when={store.collapsed && num() > 0}>
|
||||||
|
<div data-slot="question-hint" class="cursor-default mb-6">
|
||||||
|
{num()} answer{num() === 1 ? "" : "s"} selected
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<div data-slot="question-answers" hidden={store.collapsed} aria-hidden={store.collapsed}>
|
||||||
<Show when={multi()} fallback={<div data-slot="question-hint">{language.t("ui.question.singleHint")}</div>}>
|
<Show when={multi()} fallback={<div data-slot="question-hint">{language.t("ui.question.singleHint")}</div>}>
|
||||||
<div data-slot="question-hint">{language.t("ui.question.multiHint")}</div>
|
<div data-slot="question-hint">{language.t("ui.question.multiHint")}</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
@ -504,7 +569,21 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||||
onFocus={() => setStore("focus", options().length)}
|
onFocus={() => setStore("focus", options().length)}
|
||||||
onClick={customOpen}
|
onClick={customOpen}
|
||||||
>
|
>
|
||||||
<Mark multi={multi()} picked={on()} onClick={toggleCustomMark} />
|
<span
|
||||||
|
data-slot="question-option-check"
|
||||||
|
aria-hidden="true"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
customToggle()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span data-slot="question-option-box" data-type={multi() ? "checkbox" : "radio"} data-picked={on()}>
|
||||||
|
<Show when={multi()} fallback={<span data-slot="question-option-radio-dot" />}>
|
||||||
|
<Icon name="check-small" size="small" />
|
||||||
|
</Show>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
<span data-slot="question-option-main">
|
<span data-slot="question-option-main">
|
||||||
<span data-slot="option-label">{customLabel()}</span>
|
<span data-slot="option-label">{customLabel()}</span>
|
||||||
<span data-slot="option-description">{input() || customPlaceholder()}</span>
|
<span data-slot="option-description">{input() || customPlaceholder()}</span>
|
||||||
|
|
@ -563,6 +642,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||||
</form>
|
</form>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</DockPrompt>
|
</DockPrompt>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -832,7 +832,7 @@
|
||||||
[data-slot="question-body"] {
|
[data-slot="question-body"] {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 8px 8px 0;
|
padding: 8px 8px 0;
|
||||||
|
|
@ -912,7 +912,7 @@
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
line-height: var(--line-height-large);
|
line-height: var(--line-height-large);
|
||||||
color: var(--text-strong);
|
color: var(--text-strong);
|
||||||
padding: 0 10px;
|
padding: 16px 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="question-hint"] {
|
[data-slot="question-hint"] {
|
||||||
|
|
@ -1055,8 +1055,26 @@
|
||||||
line-height: var(--line-height-large);
|
line-height: var(--line-height-large);
|
||||||
color: var(--text-base);
|
color: var(--text-base);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow-wrap: anywhere;
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="question-option"][data-custom="true"] {
|
||||||
|
[data-slot="option-description"] {
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: clip;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-picked="true"] {
|
||||||
|
[data-slot="question-custom-input"]:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
outline-offset: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="question-custom"] {
|
[data-slot="question-custom"] {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue