4.4 KB143 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type {FileRev, FileStackState} from '../fileStackState';
9import type {Mode} from './FileStackEditorLines';
10
11import {Checkbox} from 'isl-components/Checkbox';
12import {Dropdown} from 'isl-components/Dropdown';
13import {RadioGroup} from 'isl-components/Radio';
14import {atom, useAtom} from 'jotai';
15import {useState} from 'react';
16import {nullthrows} from 'shared/utils';
17import {Row} from '../../ComponentUtils';
18import {EmptyState} from '../../EmptyState';
19import {t, T} from '../../i18n';
20import {FileStackEditorRow} from './FileStackEditor';
21import {bumpStackEditMetric, useStackEditState} from './stackEditState';
22
23const editModeAtom = atom<Mode>('unified-diff');
24
25export default function FileStackEditPanel() {
26 const [fileIdx, setFileIdx] = useState<null | number>(null);
27 const [mode, setMode] = useAtom(editModeAtom);
28 const [textEdit, setTextEdit] = useState(false);
29 const stackEdit = useStackEditState();
30
31 // File list dropdown.
32 const commitStack = stackEdit.commitStack.useFileStack();
33 const pathFileIdxList: Array<[string, number]> = commitStack.fileStacks
34 .map((_f, i): [string, number] => {
35 const label = commitStack.getFileStackDescription(i);
36 return [label, i];
37 })
38 .toArray()
39 .sort();
40 const fileSelector = (
41 <div
42 className="dropdown-container"
43 style={{
44 marginBottom: 'var(--pad)',
45 width: '100%',
46 minWidth: '500px',
47 zIndex: 3,
48 }}>
49 <label htmlFor="stack-file-dropdown">File to edit</label>
50 <Dropdown
51 id="stack-file-dropdown"
52 value={fileIdx == null ? 'none' : String(fileIdx)}
53 style={{width: '100%', zIndex: 3}}
54 onChange={e => {
55 const idx = e.currentTarget.value;
56 setFileIdx(idx === 'none' ? null : parseInt(idx));
57 }}
58 options={
59 [
60 {value: 'none', name: t('Select a file to edit')},
61 ...pathFileIdxList.map(([path, idx]) => ({value: String(idx), name: path})),
62 ] as Array<{value: string; name: string}>
63 }
64 />
65 </div>
66 );
67
68 if (fileIdx == null) {
69 return (
70 <div>
71 {fileSelector}
72 <EmptyState small>
73 <T>Select a file to see all changes in a row.</T>
74 </EmptyState>
75 </div>
76 );
77 }
78
79 // Properties for file stack editing.
80 const stack = nullthrows(commitStack.fileStacks.get(fileIdx));
81 const getTitle = (rev: FileRev) =>
82 commitStack.getCommitFromFileStackRev(fileIdx, rev)?.text ??
83 t(
84 '(Base version)\n\n' +
85 'Not part of the stack being edited. ' +
86 'Cannot be edited here.\n\n' +
87 'Provided to show diff against changes in the stack.',
88 );
89 const skip = (rev: FileRev) => commitStack.isAbsentFromFileStackRev(fileIdx, rev);
90 const setStack = (newStack: FileStackState) => {
91 const fileDesc = commitStack.getFileStackDescription(fileIdx);
92 const newCommitStack = commitStack.setFileStack(fileIdx, newStack);
93 stackEdit.push(newCommitStack, {name: 'fileStack', fileDesc});
94 bumpStackEditMetric('fileStackEdit');
95 };
96
97 const editorRow = (
98 <FileStackEditorRow
99 stack={stack}
100 setStack={setStack}
101 getTitle={getTitle}
102 skip={skip}
103 mode={mode}
104 textEdit={textEdit || mode === 'side-by-side-diff'}
105 />
106 );
107
108 return (
109 <div>
110 {fileSelector}
111 <div
112 style={{
113 marginLeft: 'calc(0px - var(--pad))',
114 marginRight: 'calc(0px - var(--pad))',
115 minWidth: 'calc((100vw / var(--zoom)) - 81px)',
116 minHeight: 'calc((100vh / var(--zoom)) - 265px)',
117 }}>
118 {editorRow}
119 </div>
120 <Row>
121 <RadioGroup
122 choices={[
123 {value: 'unified-diff', title: t('Unified diff')},
124 {value: 'side-by-side-diff', title: t('Side by side diff')},
125 {value: 'unified-stack', title: t('Unified stack (advanced)')},
126 ]}
127 current={mode}
128 onChange={setMode}
129 />
130 <Checkbox
131 accessKey="t"
132 checked={textEdit || mode === 'side-by-side-diff'}
133 disabled={mode === 'side-by-side-diff'}
134 onChange={() => {
135 setTextEdit(c => !c);
136 }}>
137 <T>Edit text</T>
138 </Checkbox>
139 </Row>
140 </div>
141 );
142}
143