addons/isl/src/stackEdit/ui/FileStackEditPanel.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type {FileRev, FileStackState} from '../fileStackState';
b69ab319import type {Mode} from './FileStackEditorLines';
b69ab3110
b69ab3111import {Checkbox} from 'isl-components/Checkbox';
b69ab3112import {Dropdown} from 'isl-components/Dropdown';
b69ab3113import {RadioGroup} from 'isl-components/Radio';
b69ab3114import {atom, useAtom} from 'jotai';
b69ab3115import {useState} from 'react';
b69ab3116import {nullthrows} from 'shared/utils';
b69ab3117import {Row} from '../../ComponentUtils';
b69ab3118import {EmptyState} from '../../EmptyState';
b69ab3119import {t, T} from '../../i18n';
b69ab3120import {FileStackEditorRow} from './FileStackEditor';
b69ab3121import {bumpStackEditMetric, useStackEditState} from './stackEditState';
b69ab3122
b69ab3123const editModeAtom = atom<Mode>('unified-diff');
b69ab3124
b69ab3125export default function FileStackEditPanel() {
b69ab3126 const [fileIdx, setFileIdx] = useState<null | number>(null);
b69ab3127 const [mode, setMode] = useAtom(editModeAtom);
b69ab3128 const [textEdit, setTextEdit] = useState(false);
b69ab3129 const stackEdit = useStackEditState();
b69ab3130
b69ab3131 // File list dropdown.
b69ab3132 const commitStack = stackEdit.commitStack.useFileStack();
b69ab3133 const pathFileIdxList: Array<[string, number]> = commitStack.fileStacks
b69ab3134 .map((_f, i): [string, number] => {
b69ab3135 const label = commitStack.getFileStackDescription(i);
b69ab3136 return [label, i];
b69ab3137 })
b69ab3138 .toArray()
b69ab3139 .sort();
b69ab3140 const fileSelector = (
b69ab3141 <div
b69ab3142 className="dropdown-container"
b69ab3143 style={{
b69ab3144 marginBottom: 'var(--pad)',
b69ab3145 width: '100%',
b69ab3146 minWidth: '500px',
b69ab3147 zIndex: 3,
b69ab3148 }}>
b69ab3149 <label htmlFor="stack-file-dropdown">File to edit</label>
b69ab3150 <Dropdown
b69ab3151 id="stack-file-dropdown"
b69ab3152 value={fileIdx == null ? 'none' : String(fileIdx)}
b69ab3153 style={{width: '100%', zIndex: 3}}
b69ab3154 onChange={e => {
b69ab3155 const idx = e.currentTarget.value;
b69ab3156 setFileIdx(idx === 'none' ? null : parseInt(idx));
b69ab3157 }}
b69ab3158 options={
b69ab3159 [
b69ab3160 {value: 'none', name: t('Select a file to edit')},
b69ab3161 ...pathFileIdxList.map(([path, idx]) => ({value: String(idx), name: path})),
b69ab3162 ] as Array<{value: string; name: string}>
b69ab3163 }
b69ab3164 />
b69ab3165 </div>
b69ab3166 );
b69ab3167
b69ab3168 if (fileIdx == null) {
b69ab3169 return (
b69ab3170 <div>
b69ab3171 {fileSelector}
b69ab3172 <EmptyState small>
b69ab3173 <T>Select a file to see all changes in a row.</T>
b69ab3174 </EmptyState>
b69ab3175 </div>
b69ab3176 );
b69ab3177 }
b69ab3178
b69ab3179 // Properties for file stack editing.
b69ab3180 const stack = nullthrows(commitStack.fileStacks.get(fileIdx));
b69ab3181 const getTitle = (rev: FileRev) =>
b69ab3182 commitStack.getCommitFromFileStackRev(fileIdx, rev)?.text ??
b69ab3183 t(
b69ab3184 '(Base version)\n\n' +
b69ab3185 'Not part of the stack being edited. ' +
b69ab3186 'Cannot be edited here.\n\n' +
b69ab3187 'Provided to show diff against changes in the stack.',
b69ab3188 );
b69ab3189 const skip = (rev: FileRev) => commitStack.isAbsentFromFileStackRev(fileIdx, rev);
b69ab3190 const setStack = (newStack: FileStackState) => {
b69ab3191 const fileDesc = commitStack.getFileStackDescription(fileIdx);
b69ab3192 const newCommitStack = commitStack.setFileStack(fileIdx, newStack);
b69ab3193 stackEdit.push(newCommitStack, {name: 'fileStack', fileDesc});
b69ab3194 bumpStackEditMetric('fileStackEdit');
b69ab3195 };
b69ab3196
b69ab3197 const editorRow = (
b69ab3198 <FileStackEditorRow
b69ab3199 stack={stack}
b69ab31100 setStack={setStack}
b69ab31101 getTitle={getTitle}
b69ab31102 skip={skip}
b69ab31103 mode={mode}
b69ab31104 textEdit={textEdit || mode === 'side-by-side-diff'}
b69ab31105 />
b69ab31106 );
b69ab31107
b69ab31108 return (
b69ab31109 <div>
b69ab31110 {fileSelector}
b69ab31111 <div
b69ab31112 style={{
b69ab31113 marginLeft: 'calc(0px - var(--pad))',
b69ab31114 marginRight: 'calc(0px - var(--pad))',
b69ab31115 minWidth: 'calc((100vw / var(--zoom)) - 81px)',
b69ab31116 minHeight: 'calc((100vh / var(--zoom)) - 265px)',
b69ab31117 }}>
b69ab31118 {editorRow}
b69ab31119 </div>
b69ab31120 <Row>
b69ab31121 <RadioGroup
b69ab31122 choices={[
b69ab31123 {value: 'unified-diff', title: t('Unified diff')},
b69ab31124 {value: 'side-by-side-diff', title: t('Side by side diff')},
b69ab31125 {value: 'unified-stack', title: t('Unified stack (advanced)')},
b69ab31126 ]}
b69ab31127 current={mode}
b69ab31128 onChange={setMode}
b69ab31129 />
b69ab31130 <Checkbox
b69ab31131 accessKey="t"
b69ab31132 checked={textEdit || mode === 'side-by-side-diff'}
b69ab31133 disabled={mode === 'side-by-side-diff'}
b69ab31134 onChange={() => {
b69ab31135 setTextEdit(c => !c);
b69ab31136 }}>
b69ab31137 <T>Edit text</T>
b69ab31138 </Checkbox>
b69ab31139 </Row>
b69ab31140 </div>
b69ab31141 );
b69ab31142}