mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-10 19:16:55 +02:00
Fix pair editor
This commit is contained in:
@@ -32,7 +32,6 @@ import { IconButton } from './IconButton';
|
|||||||
import type { InputHandle, InputProps } from './Input';
|
import type { InputHandle, InputProps } from './Input';
|
||||||
import { Input } from './Input';
|
import { Input } from './Input';
|
||||||
import { ensurePairId } from './PairEditor.util';
|
import { ensurePairId } from './PairEditor.util';
|
||||||
import { PlainInput } from './PlainInput';
|
|
||||||
import type { RadioDropdownItem } from './RadioDropdown';
|
import type { RadioDropdownItem } from './RadioDropdown';
|
||||||
import { RadioDropdown } from './RadioDropdown';
|
import { RadioDropdown } from './RadioDropdown';
|
||||||
|
|
||||||
@@ -80,7 +79,7 @@ export type PairWithId = Pair & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** Max number of pairs to show before prompting the user to reveal the rest */
|
/** Max number of pairs to show before prompting the user to reveal the rest */
|
||||||
const MAX_INITIAL_PAIRS = 50;
|
const MAX_INITIAL_PAIRS = 30;
|
||||||
|
|
||||||
export function PairEditor({
|
export function PairEditor({
|
||||||
allowFileValues,
|
allowFileValues,
|
||||||
@@ -190,31 +189,21 @@ export function PairEditor({
|
|||||||
[setPairsAndSave, pairs],
|
[setPairsAndSave, pairs],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFocusName = useCallback((pair: Pair) => {
|
const handleFocusName = useCallback(
|
||||||
setPairs((pairs) => {
|
(pair: Pair) => {
|
||||||
const isLast = pair.id === pairs[pairs.length - 1]?.id;
|
const isLast = pair.id === pairs[pairs.length - 1]?.id;
|
||||||
if (isLast) {
|
if (isLast) setPairs([...pairs, emptyPair()]);
|
||||||
const prevPair = pairs[pairs.length - 1];
|
},
|
||||||
rowsRef.current[prevPair?.id ?? 'n/a']?.focusName();
|
[pairs],
|
||||||
return [...pairs, emptyPair()];
|
);
|
||||||
} else {
|
|
||||||
return pairs;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleFocusValue = useCallback((pair: Pair) => {
|
const handleFocusValue = useCallback(
|
||||||
setPairs((pairs) => {
|
(pair: Pair) => {
|
||||||
const isLast = pair.id === pairs[pairs.length - 1]?.id;
|
const isLast = pair.id === pairs[pairs.length - 1]?.id;
|
||||||
if (isLast) {
|
if (isLast) setPairs([...pairs, emptyPair()]);
|
||||||
const prevPair = pairs[pairs.length - 1];
|
},
|
||||||
rowsRef.current[prevPair?.id ?? 'n/a']?.focusValue();
|
[pairs],
|
||||||
return [...pairs, emptyPair()];
|
);
|
||||||
} else {
|
|
||||||
return pairs;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
|
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
|
||||||
|
|
||||||
@@ -282,73 +271,86 @@ export function PairEditor({
|
|||||||
'-mr-2 pr-2',
|
'-mr-2 pr-2',
|
||||||
// Pad to make room for the drag divider
|
// Pad to make room for the drag divider
|
||||||
'pt-0.5',
|
'pt-0.5',
|
||||||
|
'grid grid-rows-[auto_1fr]',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<DndContext
|
<div>
|
||||||
autoScroll
|
<DndContext
|
||||||
sensors={sensors}
|
autoScroll
|
||||||
onDragMove={onDragMove}
|
sensors={sensors}
|
||||||
onDragEnd={onDragEnd}
|
onDragMove={onDragMove}
|
||||||
onDragStart={onDragStart}
|
onDragEnd={onDragEnd}
|
||||||
onDragCancel={onDragCancel}
|
onDragStart={onDragStart}
|
||||||
collisionDetection={pointerWithin}
|
onDragCancel={onDragCancel}
|
||||||
>
|
collisionDetection={pointerWithin}
|
||||||
{pairs.map((p, i) => {
|
>
|
||||||
if (!showAll && i > MAX_INITIAL_PAIRS) return null;
|
{pairs.map((p, i) => {
|
||||||
|
if (!showAll && i > MAX_INITIAL_PAIRS) return null;
|
||||||
|
|
||||||
const isLast = i === pairs.length - 1;
|
const isLast = i === pairs.length - 1;
|
||||||
return (
|
return (
|
||||||
<Fragment key={p.id}>
|
<Fragment key={p.id}>
|
||||||
{hoveredIndex === i && <DropMarker />}
|
{hoveredIndex === i && <DropMarker />}
|
||||||
<PairEditorRow
|
<PairEditorRow
|
||||||
setRef={initPairEditorRow}
|
setRef={initPairEditorRow}
|
||||||
allowFileValues={allowFileValues}
|
allowFileValues={allowFileValues}
|
||||||
allowMultilineValues={allowMultilineValues}
|
allowMultilineValues={allowMultilineValues}
|
||||||
className="py-1"
|
className="py-1"
|
||||||
forcedEnvironmentId={forcedEnvironmentId}
|
forcedEnvironmentId={forcedEnvironmentId}
|
||||||
forceUpdateKey={localForceUpdateKey}
|
forceUpdateKey={localForceUpdateKey}
|
||||||
index={i}
|
index={i}
|
||||||
isLast={isLast}
|
isLast={isLast}
|
||||||
isDraggingGlobal={!!isDragging}
|
isDraggingGlobal={!!isDragging}
|
||||||
nameAutocomplete={nameAutocomplete}
|
nameAutocomplete={nameAutocomplete}
|
||||||
nameAutocompleteFunctions={nameAutocompleteFunctions}
|
nameAutocompleteFunctions={nameAutocompleteFunctions}
|
||||||
nameAutocompleteVariables={nameAutocompleteVariables}
|
nameAutocompleteVariables={nameAutocompleteVariables}
|
||||||
namePlaceholder={namePlaceholder}
|
namePlaceholder={namePlaceholder}
|
||||||
nameValidate={nameValidate}
|
nameValidate={nameValidate}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
onFocusName={handleFocusName}
|
onFocusName={handleFocusName}
|
||||||
onFocusValue={handleFocusValue}
|
onFocusValue={handleFocusValue}
|
||||||
pair={p}
|
pair={p}
|
||||||
stateKey={stateKey}
|
stateKey={stateKey}
|
||||||
valueAutocomplete={valueAutocomplete}
|
valueAutocomplete={valueAutocomplete}
|
||||||
valueAutocompleteFunctions={valueAutocompleteFunctions}
|
valueAutocompleteFunctions={valueAutocompleteFunctions}
|
||||||
valueAutocompleteVariables={valueAutocompleteVariables}
|
valueAutocompleteVariables={valueAutocompleteVariables}
|
||||||
valuePlaceholder={valuePlaceholder}
|
valuePlaceholder={valuePlaceholder}
|
||||||
valueType={valueType}
|
valueType={valueType}
|
||||||
valueValidate={valueValidate}
|
valueValidate={valueValidate}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{!showAll && pairs.length > MAX_INITIAL_PAIRS && (
|
{!showAll && pairs.length > MAX_INITIAL_PAIRS && (
|
||||||
<Button onClick={toggleShowAll} variant="border" className="m-2" size="xs">
|
<Button onClick={toggleShowAll} variant="border" className="m-2" size="xs">
|
||||||
Show {pairs.length - MAX_INITIAL_PAIRS} More
|
Show {pairs.length - MAX_INITIAL_PAIRS} More
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<DragOverlay dropAnimation={null}>
|
|
||||||
{isDragging && (
|
|
||||||
<PairEditorRow
|
|
||||||
namePlaceholder={namePlaceholder}
|
|
||||||
valuePlaceholder={valuePlaceholder}
|
|
||||||
className="opacity-80"
|
|
||||||
pair={isDragging}
|
|
||||||
index={0}
|
|
||||||
stateKey={null}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</DragOverlay>
|
<DragOverlay dropAnimation={null}>
|
||||||
</DndContext>
|
{isDragging && (
|
||||||
|
<PairEditorRow
|
||||||
|
namePlaceholder={namePlaceholder}
|
||||||
|
valuePlaceholder={valuePlaceholder}
|
||||||
|
className="opacity-80"
|
||||||
|
pair={isDragging}
|
||||||
|
index={0}
|
||||||
|
stateKey={null}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
// There's a weird bug where clicking below one of the above Codemirror inputs will cause
|
||||||
|
// it to focus. Putting this element here prevents that
|
||||||
|
aria-hidden
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -579,44 +581,29 @@ export function PairEditorRow({
|
|||||||
'gap-0.5 grid-cols-1 grid-rows-2',
|
'gap-0.5 grid-cols-1 grid-rows-2',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isLast ? (
|
<Input
|
||||||
// Use PlainInput for last ones because there's a unique bug where clicking below
|
setRef={initNameInputRef}
|
||||||
// the Codemirror input focuses it.
|
hideLabel
|
||||||
<PlainInput
|
stateKey={`name.${pair.id}.${stateKey}`}
|
||||||
hideLabel
|
disabled={disabled}
|
||||||
size="sm"
|
wrapLines={false}
|
||||||
containerClassName={classNames(isLast && 'border-dashed')}
|
readOnly={pair.readOnlyName || isDraggingGlobal}
|
||||||
className={classNames(isDraggingGlobal && 'pointer-events-none')}
|
size="sm"
|
||||||
label="Name"
|
required={!isLast && !!pair.enabled && !!pair.value}
|
||||||
name={`name[${index}]`}
|
validate={nameValidate}
|
||||||
onFocus={handleFocusName}
|
forcedEnvironmentId={forcedEnvironmentId}
|
||||||
placeholder={namePlaceholder ?? 'name'}
|
forceUpdateKey={forceUpdateKey}
|
||||||
/>
|
containerClassName={classNames('bg-surface', isLast && 'border-dashed')}
|
||||||
) : (
|
defaultValue={pair.name}
|
||||||
<Input
|
label="Name"
|
||||||
setRef={initNameInputRef}
|
name={`name[${index}]`}
|
||||||
hideLabel
|
onChange={handleChangeName}
|
||||||
stateKey={`name.${pair.id}.${stateKey}`}
|
onFocus={handleFocusName}
|
||||||
disabled={disabled}
|
placeholder={namePlaceholder ?? 'name'}
|
||||||
wrapLines={false}
|
autocomplete={nameAutocomplete}
|
||||||
readOnly={pair.readOnlyName || isDraggingGlobal}
|
autocompleteVariables={nameAutocompleteVariables}
|
||||||
size="sm"
|
autocompleteFunctions={nameAutocompleteFunctions}
|
||||||
required={!isLast && !!pair.enabled && !!pair.value}
|
/>
|
||||||
validate={nameValidate}
|
|
||||||
forcedEnvironmentId={forcedEnvironmentId}
|
|
||||||
forceUpdateKey={forceUpdateKey}
|
|
||||||
containerClassName={classNames('bg-surface', isLast && 'border-dashed')}
|
|
||||||
defaultValue={pair.name}
|
|
||||||
label="Name"
|
|
||||||
name={`name[${index}]`}
|
|
||||||
onChange={handleChangeName}
|
|
||||||
onFocus={handleFocusName}
|
|
||||||
placeholder={namePlaceholder ?? 'name'}
|
|
||||||
autocomplete={nameAutocomplete}
|
|
||||||
autocompleteVariables={nameAutocompleteVariables}
|
|
||||||
autocompleteFunctions={nameAutocompleteFunctions}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="w-full grid grid-cols-[minmax(0,1fr)_auto] gap-1 items-center">
|
<div className="w-full grid grid-cols-[minmax(0,1fr)_auto] gap-1 items-center">
|
||||||
{pair.isFile ? (
|
{pair.isFile ? (
|
||||||
<SelectFile
|
<SelectFile
|
||||||
@@ -626,20 +613,6 @@ export function PairEditorRow({
|
|||||||
filePath={pair.value}
|
filePath={pair.value}
|
||||||
onChange={handleChangeValueFile}
|
onChange={handleChangeValueFile}
|
||||||
/>
|
/>
|
||||||
) : isLast ? (
|
|
||||||
// Use PlainInput for last ones because there's a unique bug where clicking below
|
|
||||||
// the Codemirror input focuses it.
|
|
||||||
<PlainInput
|
|
||||||
hideLabel
|
|
||||||
disabled={disabled}
|
|
||||||
size="sm"
|
|
||||||
containerClassName={classNames(isLast && 'border-dashed')}
|
|
||||||
label="Value"
|
|
||||||
name={`value[${index}]`}
|
|
||||||
className={classNames(isDraggingGlobal && 'pointer-events-none')}
|
|
||||||
onFocus={handleFocusValue}
|
|
||||||
placeholder={valuePlaceholder ?? 'value'}
|
|
||||||
/>
|
|
||||||
) : pair.value.includes('\n') ? (
|
) : pair.value.includes('\n') ? (
|
||||||
<Button
|
<Button
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
|||||||
Reference in New Issue
Block a user