Use SelectFile component in more places

This commit is contained in:
Gregory Schier
2024-07-23 11:54:35 -07:00
parent a4e223f261
commit fd2c6930f0
7 changed files with 79 additions and 44 deletions

View File

@@ -28,7 +28,7 @@ export function BinaryFileEditor({
fallback: false,
});
const handleChange = async (filePath: string | null) => {
const handleChange = async ({ filePath }: { filePath: string | null }) => {
await ignoreContentType.set(false);
onChange({ filePath: filePath ?? undefined });
};

View File

@@ -24,7 +24,7 @@ export function ImportDataDialog({ importData }: Props) {
</ul>
</VStack>
<VStack space={2}>
<SelectFile filePath={filePath} onChange={setFilePath} />
<SelectFile filePath={filePath} onChange={({ filePath }) => setFilePath(filePath)} />
{filePath && (
<Button
color="primary"

View File

@@ -1,31 +1,65 @@
import { open } from '@tauri-apps/plugin-dialog';
import classNames from 'classnames';
import mime from 'mime';
import type { ButtonProps } from './core/Button';
import { Button } from './core/Button';
import { IconButton } from './core/IconButton';
import { HStack } from './core/Stacks';
interface Props {
onChange: (filePath: string | null) => void;
type Props = ButtonProps & {
onChange: (value: { filePath: string | null; contentType: string | null }) => void;
filePath: string | null;
}
inline?: boolean;
};
export function SelectFile({ onChange, filePath }: Props) {
// Special character to insert ltr text in rtl element
const rtlEscapeChar = <>&#x200E;</>;
export function SelectFile({ onChange, filePath, inline, className }: Props) {
const handleClick = async () => {
const selected = await open({
title: 'Select File',
multiple: false,
});
if (selected == null) onChange(null);
else onChange(selected.path);
if (selected == null) return;
const filePath = selected.path;
const contentType = typeof filePath === 'string' && filePath ? mime.getType(filePath) : null;
onChange({ filePath, contentType });
};
const handleClear = async () => {
onChange({ filePath: null, contentType: null });
};
return (
<HStack space={2}>
<Button variant="border" color="secondary" size="sm" onClick={handleClick}>
Choose File
<HStack space={1.5} className="group relative justify-stretch">
<Button
className={classNames(className, 'font-mono text-xs rtl', inline && 'w-full')}
color="secondary"
size="sm"
onClick={handleClick}
>
{rtlEscapeChar}
{inline ? <>{filePath || 'Select File'}</> : <>Select File</>}
</Button>
<div className="text-sm font-mono truncate rtl pr-3 text-fg">
{/* Special character to insert ltr text in rtl element without making things wonky */}
&#x200E;
{filePath ?? 'No file selected'}
</div>
{!inline && (
<>
{filePath && (
<IconButton
size="sm"
variant="border"
icon="x"
title="Unset File"
onClick={handleClear}
/>
)}
<div className="text-sm font-mono truncate rtl pr-3 text-fg">
{rtlEscapeChar}
{filePath ?? 'No file selected'}
</div>
</>
)}
</HStack>
);
}

View File

@@ -5,7 +5,7 @@ import type { HotkeyAction } from '../../hooks/useHotKey';
import { useFormattedHotkey, useHotKey } from '../../hooks/useHotKey';
import { Icon } from './Icon';
export type ButtonProps = Omit<HTMLAttributes<HTMLButtonElement>, 'color'> & {
export type ButtonProps = Omit<HTMLAttributes<HTMLButtonElement>, 'color' | 'onChange'> & {
innerClassName?: string;
color?:
| 'custom'

View File

@@ -1,4 +1,3 @@
import { open } from '@tauri-apps/plugin-dialog';
import classNames from 'classnames';
import type { EditorView } from 'codemirror';
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -7,7 +6,7 @@ import { useDrag, useDrop } from 'react-dnd';
import { v4 as uuid } from 'uuid';
import { usePrompt } from '../../hooks/usePrompt';
import { DropMarker } from '../DropMarker';
import { Button } from './Button';
import { SelectFile } from '../SelectFile';
import { Checkbox } from './Checkbox';
import { Dropdown } from './Dropdown';
import type { GenericCompletionConfig } from './Editor/genericCompletion';
@@ -286,7 +285,12 @@ function PairEditorRow({
);
const handleChangeValueFile = useMemo(
() => (value: string) => onChange({ id, pair: { ...pairContainer.pair, value, isFile: true } }),
() =>
({ filePath }: { filePath: string | null }) =>
onChange({
id,
pair: { ...pairContainer.pair, value: filePath ?? '', isFile: true },
}),
[onChange, id, pairContainer.pair],
);
@@ -386,27 +390,12 @@ function PairEditorRow({
/>
<div className="w-full grid grid-cols-[minmax(0,1fr)_auto] gap-1 items-center">
{pairContainer.pair.isFile ? (
<Button
<SelectFile
inline
size="xs"
color="secondary"
className="font-mono text-2xs rtl"
onClick={async (e) => {
e.preventDefault();
const selected = await open({
title: 'Select file',
multiple: false,
});
if (selected == null) {
return;
}
handleChangeValueFile(selected.path);
}}
>
{/* Special character to insert ltr text in rtl element without making things wonky */}
&#x200E;
{pairContainer.pair.value || 'Select File'}
</Button>
filePath={pairContainer.pair.value}
onChange={handleChangeValueFile}
/>
) : (
<Input
hideLabel
@@ -432,7 +421,7 @@ function PairEditorRow({
<RadioDropdown
value={pairContainer.pair.isFile ? 'file' : 'text'}
onChange={(v) => {
if (v === 'file') handleChangeValueFile('');
if (v === 'file') handleChangeValueFile({ filePath: '' });
else handleChangeValueText('');
}}
items={[
@@ -444,6 +433,7 @@ function PairEditorRow({
key: 'mime',
label: 'Set Content-Type',
leftSlot: <Icon icon="pencil" />,
hidden: !pairContainer.pair.isFile,
onSelect: async () => {
const v = await prompt({
id: 'content-type',
@@ -459,6 +449,15 @@ function PairEditorRow({
handleChangeValueContentType(v);
},
},
{
key: 'clear-file',
label: 'Unset File',
leftSlot: <Icon icon="x" />,
hidden: !pairContainer.pair.isFile,
onSelect: async () => {
handleChangeValueFile({ filePath: null });
},
},
{
key: 'delete',
label: 'Delete',

View File

@@ -60,10 +60,11 @@ type BaseStackProps = HTMLAttributes<HTMLElement> & {
space?: keyof typeof gapClasses;
alignItems?: 'start' | 'center' | 'stretch' | 'end';
justifyContent?: 'start' | 'center' | 'end' | 'between';
wrap?: boolean;
};
const BaseStack = forwardRef(function BaseStack(
{ className, alignItems, justifyContent, children, as, ...props }: BaseStackProps,
{ className, alignItems, justifyContent, wrap, children, as, ...props }: BaseStackProps,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ref: ForwardedRef<any>,
) {
@@ -74,6 +75,7 @@ const BaseStack = forwardRef(function BaseStack(
className={classNames(
className,
'flex',
wrap && 'flex-wrap',
alignItems === 'center' && 'items-center',
alignItems === 'start' && 'items-start',
alignItems === 'stretch' && 'items-stretch',

View File

@@ -127,7 +127,7 @@ export function TextViewer({ response, pretty, className }: Props) {
return <BinaryViewer response={response} />;
}
if (!showLargeResponse && (response.contentLength ?? 0) > LARGE_RESPONSE_BYTES / 1000) {
if (!showLargeResponse && (response.contentLength ?? 0) > LARGE_RESPONSE_BYTES) {
return (
<Banner color="primary" className="h-full flex flex-col gap-3">
<p>
@@ -137,7 +137,7 @@ export function TextViewer({ response, pretty, className }: Props) {
</InlineCode>{' '}
may impact performance
</p>
<HStack space={2}>
<HStack wrap space={2}>
<Button color="primary" size="xs" onClick={toggleShowLargeResponse}>
Reveal Response
</Button>