Improve <details> component (#238)

Co-authored-by: Gregory Schier <gschier1990@gmail.com>
This commit is contained in:
Song
2025-07-19 05:28:24 +08:00
committed by GitHub
parent 4a2fb6ed48
commit 0d4b7bb5e2
6 changed files with 95 additions and 65 deletions

View File

@@ -20,6 +20,7 @@ import { showDialog } from '../lib/dialog';
import { resolvedModelName } from '../lib/resolvedModelName';
import { Banner } from './core/Banner';
import { Checkbox } from './core/Checkbox';
import { DetailsBanner } from './core/DetailsBanner';
import { Editor } from './core/Editor/Editor';
import { IconButton } from './core/IconButton';
import { Input } from './core/Input';
@@ -174,22 +175,23 @@ function FormInputs<T extends Record<string, JsonPrimitive>>({
);
case 'accordion':
return (
<Banner key={i} className={classNames('!p-0', disabled && 'opacity-disabled')}>
<details>
<summary className="px-3 py-1.5 text-text-subtle">{input.label}</summary>
<div className="mb-3 px-3">
<FormInputs
data={data}
disabled={disabled}
inputs={input.inputs}
setDataAttr={setDataAttr}
stateKey={stateKey}
autocompleteFunctions={autocompleteFunctions || false}
autocompleteVariables={autocompleteVariables}
/>
</div>
</details>
</Banner>
<DetailsBanner
key={i}
summary={input.label}
className={classNames(disabled && 'opacity-disabled')}
>
<div className="mb-3 px-3">
<FormInputs
data={data}
disabled={disabled}
inputs={input.inputs}
setDataAttr={setDataAttr}
stateKey={stateKey}
autocompleteFunctions={autocompleteFunctions || false}
autocompleteVariables={autocompleteVariables}
/>
</div>
</DetailsBanner>
);
case 'banner':
return (

View File

@@ -7,9 +7,9 @@ import slugify from 'slugify';
import { activeWorkspaceAtom } from '../hooks/useActiveWorkspace';
import { pluralizeCount } from '../lib/pluralize';
import { invokeCmd } from '../lib/tauri';
import { Banner } from './core/Banner';
import { Button } from './core/Button';
import { Checkbox } from './core/Checkbox';
import { DetailsBanner } from './core/DetailsBanner';
import { HStack, VStack } from './core/Stacks';
interface Props {
@@ -123,19 +123,14 @@ function ExportDataDialogContent({
))}
</tbody>
</table>
<Banner className="!p-0">
<details open>
<summary className="px-3 py-2">Extra Settings</summary>
<div className="px-3 pb-2">
<Checkbox
checked={includePrivateEnvironments}
onChange={setIncludePrivateEnvironments}
title="Include private environments"
help='Environments marked as "sharable" will be exported by default'
/>
</div>
</details>
</Banner>
<DetailsBanner color="secondary" open summary="Extra Settings">
<Checkbox
checked={includePrivateEnvironments}
onChange={setIncludePrivateEnvironments}
title="Include private environments"
help='Environments marked as "sharable" will be exported by default'
/>
</DetailsBanner>
<HStack space={2} justifyContent="end">
<Button className="focus" variant="border" onClick={onHide}>
Cancel

View File

@@ -5,8 +5,8 @@ import { connections } from '../lib/data/connections';
import { encodings } from '../lib/data/encodings';
import { headerNames } from '../lib/data/headerNames';
import { mimeTypes } from '../lib/data/mimetypes';
import { Banner } from './core/Banner';
import { CountBadge } from './core/CountBadge';
import { DetailsBanner } from './core/DetailsBanner';
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
import type { InputProps } from './core/Input';
import type { Pair, PairEditorProps } from './core/PairEditor';
@@ -35,35 +35,36 @@ export function HeadersEditor({
return (
<div className="@container w-full h-full grid grid-rows-[auto_minmax(0,1fr)]">
{validInheritedHeaders.length > 0 ? (
<Banner className="!py-0 mb-1.5 border-dashed" color="secondary">
<details>
<summary className="py-1.5 text-sm !cursor-default !select-none opacity-70 hover:opacity-100">
<HStack>
Inherited <CountBadge count={validInheritedHeaders.length} />
</HStack>
</summary>
<div className="pb-2">
{validInheritedHeaders?.map((pair, i) => (
<PairEditorRow
key={pair.id + '.' + i}
index={i}
disabled
disableDrag
className="py-1"
onChange={() => {}}
onEnd={() => {}}
onMove={() => {}}
pair={ensurePairId(pair)}
stateKey={null}
nameAutocompleteFunctions
nameAutocompleteVariables
valueAutocompleteFunctions
valueAutocompleteVariables
/>
))}
</div>
</details>
</Banner>
<DetailsBanner
color="secondary"
className="text-sm mb-1.5"
summary={
<HStack>
Inherited <CountBadge count={validInheritedHeaders.length} />
</HStack>
}
>
<div className="pb-2">
{validInheritedHeaders?.map((pair, i) => (
<PairEditorRow
key={pair.id + '.' + i}
index={i}
disabled
disableDrag
className="py-1"
onChange={() => {}}
onEnd={() => {}}
onMove={() => {}}
pair={ensurePairId(pair)}
stateKey={null}
nameAutocompleteFunctions
nameAutocompleteVariables
valueAutocompleteFunctions
valueAutocompleteVariables
/>
))}
</div>
</DetailsBanner>
) : (
<span />
)}

View File

@@ -1,4 +1,5 @@
import { Button } from './core/Button';
import { DetailsBanner } from './core/DetailsBanner';
import { FormattedError } from './core/FormattedError';
import { Heading } from './core/Heading';
import { VStack } from './core/Stacks';
@@ -17,10 +18,9 @@ export default function RouteError({ error }: { error: unknown }) {
<FormattedError>
{message}
{stack && (
<details className="mt-3 select-auto text-xs">
<summary className="!cursor-default !select-none">Stack Trace</summary>
<DetailsBanner color="secondary" className="mt-3 select-auto text-xs" summary="Stack Trace">
<div className="mt-2 text-xs">{stack}</div>
</details>
</DetailsBanner>
)}
</FormattedError>
<VStack space={2}>

View File

@@ -13,9 +13,10 @@ export function Banner({ children, className, color }: BannerProps) {
<div
className={classNames(
className,
color && 'bg-surface',
`x-theme-banner--${color}`,
'border border-border bg-surface',
'px-4 py-3 rounded-lg select-auto',
'border border-border border-dashed',
'px-4 py-2 rounded-lg select-auto',
'overflow-auto text-text',
)}
>

View File

@@ -0,0 +1,31 @@
import classNames from 'classnames';
import type { HTMLAttributes, ReactNode } from 'react';
import type { BannerProps } from './Banner';
import { Banner } from './Banner';
interface Props extends HTMLAttributes<HTMLDetailsElement> {
summary: ReactNode;
color?: BannerProps['color'];
open?: boolean;
}
export function DetailsBanner({ className, color, summary, children, ...extraProps }: Props) {
return (
<Banner color={color} className={className}>
<details className="group list-none" {...extraProps}>
<summary className="!cursor-default !select-none list-none flex items-center gap-2 focus:outline-none opacity-70 hover:opacity-100 focus:opacity-100">
<div
className={classNames(
'transition-transform',
'group-open:rotate-90',
'w-0 h-0 border-t-[0.3em] border-b-[0.3em] border-l-[0.5em] border-r-0',
'border-t-transparent border-b-transparent border-l-text-subtle',
)}
></div>
{summary}
</summary>
{children}
</details>
</Banner>
);
}