Skip to content

Commit d9c0012

Browse files
committed
fix: Fixed escape behaviour on the main page (now it will reset focus to the first element). Fixed indexing so that everything is now 0 indexed for focused element. And fixed focus behaviour for exiting field with escape
1 parent dd0de53 commit d9c0012

3 files changed

Lines changed: 21 additions & 14 deletions

File tree

src/Form.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const Form: React.FC<FormProps> = props => {
2121
const [editingField, setEditingField] = useState<string>();
2222
const canSubmitForm = useMemo(() => canSubmit(form, value), [value, form]);
2323
const focusManager = useFocusManager();
24-
const [focusedElement, setFocusedElement] = useState(0);
24+
const [focusedElement, setFocusedElement] = useState(-1); // this is -1 because there is no initial focus. The first down click will focus on element 0
2525
const headerRef = useRef();
2626
const headerHeight = headerRef.current ? measureElement(headerRef.current).height : 5
2727
const [, fullHeight] = useStdoutDimensions()
@@ -47,19 +47,15 @@ export const Form: React.FC<FormProps> = props => {
4747
}
4848
}, []);
4949

50-
const onSetEditingField = (field?: string) => {
51-
const isEditing = !!editingField;
52-
if (!isEditing && field) {
53-
const elementNum = sections[currentTab].fields.findIndex((f) => f.name === field);
54-
setFocusedElement(elementNum + 1);
55-
}
56-
57-
setEditingField(field);
50+
const onFieldExit = (fieldName) => {
51+
const elementNum = sections[currentTab].fields.findIndex((f) => f.name === fieldName);
52+
focusManager.focus(`${elementNum}`)
53+
setFocusedElement(elementNum);
5854
}
5955

6056
const onChangeTab = (tab: number) => {
6157
setCurrentTab(tab);
62-
setFocusedElement(0)
58+
setFocusedElement(-1)
6359
}
6460

6561
const setValueAndPropagate = (index: number, newValue: Record<string, unknown>) => {
@@ -71,7 +67,7 @@ export const Form: React.FC<FormProps> = props => {
7167
useInput(
7268
(input, key) => {
7369
if (key.upArrow) {
74-
if (focusedElement - 1 <= 0) {
70+
if (focusedElement <= 0) {
7571
return;
7672
}
7773

@@ -80,12 +76,19 @@ export const Form: React.FC<FormProps> = props => {
8076
} else if (key.downArrow) {
8177
// This calculates the maximum amount of children there is. We don't want to scroll past the last item
8278
// Fields.length is number of json fields. 2 is the add and remove buttons. Submit button is sometimes focusable
83-
if (focusedElement + 1 > sections[currentTab].fields.length + 2 + (canSubmitForm ? 1 : 0)) {
79+
if (focusedElement + 1 >= sections[currentTab].fields.length + 2 + (canSubmitForm ? 1 : 0)) {
8480
return;
8581
}
8682

8783
setFocusedElement((focusedElement) => focusedElement + 1);
8884
focusManager.focusNext();
85+
return;
86+
}
87+
88+
// When escape is pressed the focus is automatically reset. We have to handle that case or else the ordering gets messed up.
89+
if (key.escape) {
90+
focusManager.focus('0');
91+
setFocusedElement(0);
8992
}
9093
},
9194
{ isActive: !editingField }
@@ -116,7 +119,7 @@ export const Form: React.FC<FormProps> = props => {
116119
<FullScreen>
117120
<Box width="100%" height="90%" flexDirection="column" overflowY="hidden">
118121
<FormHeader {...props} headerRef={headerRef} form={form} currentTab={currentTab} onChangeTab={onChangeTab} editingField={editingField} />
119-
<ScrollArea height={fullHeight - headerHeight} key={currentTab} isStart={focusedElement === 0} editingMode={!!editingField}>
122+
<ScrollArea height={fullHeight - headerHeight} key={currentTab} isStart={focusedElement === -1} editingMode={!!editingField}>
120123
{!editingField && sections[currentTab].description && (
121124
<Box marginX={4}>
122125
<DescriptionRenderer description={sections[currentTab]?.description} />
@@ -140,8 +143,9 @@ export const Form: React.FC<FormProps> = props => {
140143
key={field.name + currentTab}
141144
form={form}
142145
value={value[currentTab][field.name]}
146+
onExit={onFieldExit}
143147
onChange={v => setValueAndPropagate(currentTab, { ...value[currentTab], [field.name]: v })}
144-
onSetEditingField={onSetEditingField}
148+
onSetEditingField={setEditingField}
145149
editingField={editingField}
146150
customManagers={props.customManagers}
147151
/>

src/FormFieldRenderer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ export const FormFieldRenderer: React.FC<FormFieldRendererProps<any>> = props =>
2121
props.onChange(currentValue);
2222
}
2323
props.onSetEditingField(undefined);
24+
props.onExit(props.field.name)
2425
}
2526
};
2627

2728
const cancel = () => {
2829
setCurrentValue(props.value);
2930
props.onSetEditingField(undefined);
3031
setError(undefined);
32+
props.onExit(props.field.name)
3133
};
3234

3335
const { isFocused } = useFocus({id: props.id});

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export type FormFieldRendererProps<T extends FormField> = {
189189
form: FormStructure;
190190
value?: ValueOfField<T>;
191191
onChange: (value: ValueOfField<T>) => void;
192+
onExit: (fieldName: string) => void;
192193
onSetEditingField: (field?: string) => void;
193194
editingField?: string;
194195
customManagers?: FormFieldManager<FormField>[];

0 commit comments

Comments
 (0)