Clean up Component Utils, use stylex

Summary:
I noticed we had both <Row> and <FlexRow>, ostensibly doing the same thing...?

Here I combine these, and rewrite the API to allow passing xstyle with stylex styles. This makes it easier to use these components when using stylex.

Reviewed By: quark-zju

Differential Revision: D54391157

fbshipit-source-id: e4f88aaac2d8564967b27b904f23b0b9b308ee00
This commit is contained in:
Evan Krause 2024-03-01 11:50:14 -08:00 committed by Facebook GitHub Bot
parent 05c8e3fa70
commit 1e49f1336e
10 changed files with 86 additions and 85 deletions

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {FlexRow} from './ComponentUtils';
import {Row} from './ComponentUtils';
import {Tooltip} from './Tooltip';
import {t, T} from './i18n';
import {configBackedAtom} from './jotaiUtils';
@ -41,11 +41,11 @@ export function ChangedFileDisplayTypePicker() {
const actions = entries.map(([type, options]) => ({
label: (
<FlexRow>
<Row>
<Icon icon={displayType === type ? 'check' : 'blank'} slot="start" />
<Icon icon={options.icon} slot="start" />
{options.label}
</FlexRow>
</Row>
),
onClick: () => setDisplayType(type),
}));

View File

@ -11,7 +11,7 @@ import type {ContextMenuItem} from 'shared/ContextMenu';
import {commitMode, hasUnsavedEditedCommitMessage} from './CommitInfoView/CommitInfoState';
import {currentComparisonMode} from './ComparisonView/atoms';
import {FlexRow} from './ComponentUtils';
import {Row} from './ComponentUtils';
import {EducationInfoTip} from './Education';
import {HighlightCommitsWhileHovering} from './HighlightedCommits';
import {InlineBadge} from './InlineBadge';
@ -731,14 +731,14 @@ export function SuccessorInfoToDisplay({successorInfo}: {successorInfo: Successo
}[successorType] ?? <T>Rewritten as a newer commit</T>;
const isSuccessorPublic = successorType === 'land' || successorType === 'pushrebase';
return (
<FlexRow style={{gap: 'var(--halfpad)'}}>
<Row style={{gap: 'var(--halfpad)'}}>
<HighlightCommitsWhileHovering toHighlight={[successorInfo.hash]}>
{inner}
</HighlightCommitsWhileHovering>
<EducationInfoTip>
<ObsoleteTip isSuccessorPublic={isSuccessorPublic} />
</EducationInfoTip>
</FlexRow>
</Row>
);
}

View File

@ -5,26 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
.center-container {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
}
.flex-row {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--pad);
}
/* Workaround missing `scrollbar-width: none` support in Chrome */
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.spacer {
flex-grow: 1;
}

View File

@ -5,10 +5,31 @@
* LICENSE file in the root directory of this source tree.
*/
import {spacing} from './tokens.stylex';
import * as stylex from '@stylexjs/stylex';
import {Icon} from 'shared/Icon';
import {notEmpty} from 'shared/utils';
import './ComponentUtils.css';
const styles = stylex.create({
center: {
display: 'flex',
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
},
flex: {
display: 'flex',
alignItems: 'center',
gap: spacing.pad,
},
spacer: {
flexGrow: 1,
},
});
export function LargeSpinner() {
return (
<div data-testid="loading-spinner">
@ -18,26 +39,18 @@ export function LargeSpinner() {
}
export function Center(props: ContainerProps) {
const className = addClassName('center-container', props);
return (
<div {...props} className={className}>
{props.children}
</div>
);
}
export function FlexRow(props: FlexProps) {
return FlexBox({...props, className: addClassName('flex-row', props)});
const {className, ...rest} = props;
return <div {...stylexPropsWithClassName(styles.center, className)} {...rest} />;
}
/** Flexbox container with horizontal children. */
export function Row(props: FlexProps) {
return FlexBox({...props, direction: 'row'});
export function Row(props: ContainerProps) {
return FlexBox(props, 'row');
}
/** Flexbox container with vertical children. */
export function Column(props: FlexProps) {
return FlexBox({...props, direction: 'column'});
export function Column(props: ContainerProps) {
return FlexBox(props, 'column');
}
/** Container that scrolls horizontally. */
@ -52,27 +65,24 @@ export function ScrollY(props: ScrollProps) {
/** Visually empty flex item with `flex-grow: 1` to insert as much space as possible between siblings. */
export function FlexSpacer() {
return <div className={'spacer'} />;
return <div {...stylex.props(styles.spacer)} />;
}
type ContainerProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
type FlexProps = ContainerProps & {
direction?: 'row' | 'column';
};
type ContainerProps = React.DetailedHTMLProps<
React.HTMLAttributes<HTMLDivElement>,
HTMLDivElement
> & {xstyle?: stylex.StyleXStyles};
/** See `<Row>` and `<Column>`. */
function FlexBox(props: FlexProps) {
const direction = props.direction ?? 'row';
const style: React.CSSProperties = {
display: 'flex',
flexDirection: direction,
flexWrap: 'nowrap',
};
const mergedProps = {...props, style: {...style, ...props.style}};
delete mergedProps.children;
delete mergedProps.direction;
return <div {...mergedProps}>{props.children}</div>;
function FlexBox(props: ContainerProps, flexDirection: 'row' | 'column') {
const {className, style, ...rest} = props;
return (
<div
{...stylexPropsWithClassName([styles.flex, props.xstyle].filter(notEmpty), className)}
{...rest}
style={{flexDirection, ...style}}
/>
);
}
type ScrollProps = ContainerProps & {
@ -130,6 +140,15 @@ function Scroll(props: ScrollProps) {
);
}
function addClassName(name: string, props: FlexProps) {
return props.className == null ? name : `${props.className} ${name}`;
/**
* Like stylex.props(), but also adds in extra classNames.
* Useful since `{...stylex.props()}` sets className,
* and either overwrites or is overwritten by other `className="..."` props.
*/
export function stylexPropsWithClassName(
style: stylex.StyleXStyles,
...names: Array<string | undefined>
) {
const {className, ...rest} = stylex.props(style);
return {...rest, className: className + ' ' + names.filter(notEmpty).join(' ')};
}

View File

@ -9,7 +9,7 @@ import type {ThemeColor} from './theme';
import type {PreferredSubmitCommand} from './types';
import type {ReactNode} from 'react';
import {FlexRow} from './ComponentUtils';
import {Row} from './ComponentUtils';
import {confirmShouldSubmitEnabledAtom} from './ConfirmSubmitStack';
import {DropdownField, DropdownFields} from './DropdownFields';
import {useShowKeyboardShortcutsHelp} from './ISLShortcuts';
@ -264,13 +264,13 @@ function OpenFilesCmdSetting() {
<pre>sl config --user isl.open-file-cmd '["cmd", "with", "args"]'</pre>
</div>
)}>
<FlexRow>
<Row>
<T replace={{$cmd: cmdEl}}>Open files in $cmd</T>
<Subtle>
<T>How to configure?</T>
</Subtle>
<Icon icon="question" />
</FlexRow>
</Row>
</Tooltip>
);
}

View File

@ -9,7 +9,7 @@ import type {DagCommitInfo} from './dag/dag';
import type {Hash} from './types';
import {CleanupButton, isStackEligibleForCleanup} from './Cleanup';
import {FlexRow} from './ComponentUtils';
import {Row} from './ComponentUtils';
import {shouldShowSubmitStackConfirmation, useShowConfirmSubmitStack} from './ConfirmSubmitStack';
import {HighlightCommitsWhileHovering} from './HighlightedCommits';
import {OperationDisabledButton} from './OperationDisabledButton';
@ -154,10 +154,10 @@ export function StackActions({hash}: {hash: Hash}): React.ReactElement | null {
moreActions.push({
label: (
<HighlightCommitsWhileHovering key="submit-entire-stack" toHighlight={submittableStack}>
<FlexRow>
<Row>
<Icon icon="cloud-upload" slot="start" />
<T>Submit entire stack</T>
</FlexRow>
</Row>
</HighlightCommitsWhileHovering>
),
onClick: async () => {

View File

@ -11,7 +11,7 @@ import type {ParsedDiff} from 'shared/patch/parse';
import {AvatarImg} from '../Avatar';
import serverAPI from '../ClientToServerAPI';
import {SplitDiffTable} from '../ComparisonView/SplitDiffView/SplitDiffHunk';
import {Column, FlexRow} from '../ComponentUtils';
import {Column, Row} from '../ComponentUtils';
import {ErrorNotice} from '../ErrorNotice';
import {Link} from '../Link';
import {Subtle} from '../Subtle';
@ -65,6 +65,7 @@ const styles = stylex.create({
commentInfo: {
gap: spacing.half,
marginBlock: spacing.half,
alignItems: 'flex-start',
},
inlineCommentFilename: {
marginBottom: spacing.half,
@ -96,7 +97,7 @@ const styles = stylex.create({
function Comment({comment, isTopLevel}: {comment: DiffComment; isTopLevel?: boolean}) {
return (
<FlexRow {...stylex.props(styles.comment)}>
<Row xstyle={styles.comment}>
<Column {...stylex.props(styles.left)}>
<AvatarImg
username={comment.author}
@ -104,7 +105,7 @@ function Comment({comment, isTopLevel}: {comment: DiffComment; isTopLevel?: bool
{...stylex.props(styles.avatar)}
/>
</Column>
<Column {...stylex.props(styles.commentInfo)}>
<Column xstyle={styles.commentInfo}>
<b {...stylex.props(styles.author)}>{comment.author}</b>
<div>
{isTopLevel && comment.filename && (
@ -130,7 +131,7 @@ function Comment({comment, isTopLevel}: {comment: DiffComment; isTopLevel?: bool
<Comment key={i} comment={reply} />
))}
</Column>
</FlexRow>
</Row>
);
}
@ -184,10 +185,10 @@ function Reactions({reactions}: {reactions: Array<DiffCommentReaction>}) {
const names = reactions.map(r => r.name);
return (
<Tooltip title={names.join(', ')}>
<FlexRow style={{gap: spacing.half}}>
<Row style={{gap: spacing.half}}>
<span style={{letterSpacing: '-2px'}}>{icons}</span>
<span>{total}</span>
</FlexRow>
</Row>
</Tooltip>
);
}

View File

@ -11,7 +11,7 @@ import type {ExclusiveOr} from 'shared/typeUtils';
import {holdingAltAtom} from '../ChangedFile';
import {debugLogMessageTraffic} from '../ClientToServerAPI';
import {FlexRow} from '../ComponentUtils';
import {Row} from '../ComponentUtils';
import {DropdownField, DropdownFields} from '../DropdownFields';
import {InlineErrorBadge} from '../ErrorNotice';
import messageBus from '../MessageBus';
@ -69,10 +69,10 @@ export default function DebugToolsMenu({dismiss}: {dismiss: () => unknown}) {
</DropdownField>
<DropdownField title={<T>Server/Client Messages</T>}>
<ServerClientMessageLogging />
<FlexRow>
<Row>
<ForceDisconnectButton />
<NopOperationButtons />
</FlexRow>
</Row>
</DropdownField>
<DropdownField title={<T>Component Explorer</T>}>
<ComponentExplorerButton dismiss={dismiss} />
@ -97,7 +97,7 @@ function InternalState() {
return (
<div>
<FlexRow>
<Row>
<Tooltip
placement="bottom"
title={t(
@ -136,9 +136,9 @@ function InternalState() {
<T>Clear Persisted State</T>
</VSCodeButton>
</Tooltip>
</FlexRow>
</Row>
{isDev && (
<FlexRow>
<Row>
<T>Integrate with: </T>
<VSCodeCheckbox checked={reduxTools} onChange={() => setReduxTools(v => !v)}>
<T>Redux DevTools</T>
@ -146,7 +146,7 @@ function InternalState() {
<VSCodeCheckbox checked={reactTools} onChange={() => setReactTools(v => !v)}>
{t('React <DebugAtoms/>')}
</VSCodeCheckbox>
</FlexRow>
</Row>
)}
</div>
);

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {FlexRow, FlexSpacer, ScrollY} from '../../ComponentUtils';
import {Row, FlexSpacer, ScrollY} from '../../ComponentUtils';
import {Modal} from '../../Modal';
import {tracker} from '../../analytics';
import {T} from '../../i18n';
@ -41,9 +41,9 @@ function LoadedSplitModal() {
return (
<Modal>
<SplitStackEditPanel />
<FlexRow style={{padding: 'var(--pad) 0', justifyContent: 'flex-end'}}>
<Row style={{padding: 'var(--pad) 0', justifyContent: 'flex-end'}}>
<StackEditConfirmButtons />
</FlexRow>
</Row>
</Modal>
);
}
@ -106,11 +106,11 @@ function LoadedEditStackModal() {
{activeTab === 'split' && <SplitStackEditPanel />}
</VSCodePanelView>
</VSCodePanels>
<FlexRow style={{padding: 'var(--pad) 0', justifyContent: 'flex-end'}}>
<Row style={{padding: 'var(--pad) 0', justifyContent: 'flex-end'}}>
{activeTab === 'split' && <SplitStackToolbar />}
<FlexSpacer />
<StackEditConfirmButtons />
</FlexRow>
</Row>
</Modal>
);
}

View File

@ -12,7 +12,7 @@ import type {StackEditOpDescription, UseStackEditState} from './stackEditState';
import {AnimatedReorderGroup} from '../../AnimatedReorderGroup';
import {CommitTitle as StandaloneCommitTitle} from '../../CommitTitle';
import {FlexRow} from '../../ComponentUtils';
import {Row} from '../../ComponentUtils';
import {DragHandle} from '../../DragHandle';
import {Tooltip} from '../../Tooltip';
import {t, T} from '../../i18n';
@ -266,7 +266,7 @@ export function StackEditCommit({
);
return (
<FlexRow
<Row
data-reorder-id={onDrag ? commit.key : ''}
data-rev={rev}
className={`commit${isReorderPreview ? ' commit-reorder-preview' : ''}`}>
@ -276,7 +276,7 @@ export function StackEditCommit({
{buttons}
{title}
{rightSideButtons}
</FlexRow>
</Row>
);
}