Skip to content

Commit

Permalink
Fix editable text style (#6799)
Browse files Browse the repository at this point in the history
* refactored input component TS types

* fix CSS line breaks for dataset description + name

* fix some types

* fix segment list line breaks

* fixed lined breaks in segments list

* applied PR feedback

* fix ENTER key for multiline text inputs
  • Loading branch information
hotzenklotz authored Feb 8, 2023
1 parent e2036fd commit 8a996ac
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 125 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
### Fixed
- Fixed saving allowed teams in dataset settings. [#6817](https://github.com/scalableminds/webknossos/pull/6817)
- Fixed log streaming in Voxelytics workflow reports. [#6828](https://github.com/scalableminds/webknossos/pull/6828)
- Fixed some layouting issues with line breaks in segment list/dataset info tab [#6799](https://github.com/scalableminds/webknossos/pull/6799)

### Removed

Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/admin/dataset/dataset_upload_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ class DatasetUploadView extends React.Component<PropsWithFormAndRouter, State> {
width: 400,
}}
allowDecimals
onChange={(scale) => {
onChange={(scale: Vector3) => {
if (this.formRef.current == null) return;
this.formRef.current.setFieldsValue({
scale,
Expand Down
6 changes: 3 additions & 3 deletions frontend/javascripts/components/text_with_description.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import Markdown from "react-remarkable";
import * as React from "react";
import type { EditableTextLabelProp } from "oxalis/view/components/editable_text_label";
import EditableTextLabel from "oxalis/view/components/editable_text_label";

type EditableProps = EditableTextLabelProp & {
isEditable: true;
description: string;
};
type NonEditableProps = {
markdown?: boolean;
isEditable: false;
description: string;
value: string;
Expand Down Expand Up @@ -61,8 +63,7 @@ class TextWithDescription extends React.PureComponent<Props> {
</Tooltip>
) : null}
</span>
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message */}
<span className={hasDescription ? "flex-item" : null}>
<span className={hasDescription ? "flex-item" : undefined}>
{isEditable ? (
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
<EditableTextLabel {...editableProps} />
Expand All @@ -73,7 +74,6 @@ class TextWithDescription extends React.PureComponent<Props> {
display: "inline-block",
}}
>
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'markdown' does not exist on type 'Readon... Remove this comment to see the full error message */}
{this.props.markdown ? (
<Markdown
source={this.props.value}
Expand Down
17 changes: 10 additions & 7 deletions frontend/javascripts/libs/vector_input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import * as React from "react";
import _ from "lodash";
import type { ServerBoundingBoxTypeTuple } from "types/api_flow_types";
import type { Vector3, Vector6 } from "oxalis/constants";
import InputComponent, { InputComponentProps } from "oxalis/view/components/input_component";
import InputComponent, {
InputComponentCommonProps,
InputElementProps,
} from "oxalis/view/components/input_component";
import * as Utils from "libs/utils";

type BaseProps<T> = Omit<InputComponentProps, "value"> & {
type BaseProps<T> = Omit<InputComponentCommonProps & InputElementProps, "value" | "onChange"> & {
value: T | string;
onChange: (value: T) => void;
changeOnlyOnBlur?: boolean;
allowDecimals?: boolean;
autoSize?: boolean;
};
type State = {
isEditing: boolean;
Expand Down Expand Up @@ -51,7 +55,7 @@ class BaseVector<T extends Vector3 | Vector6> extends React.PureComponent<BasePr
return value;
}

handleBlur = () => {
handleBlur = (_: React.FocusEvent<HTMLInputElement>) => {
this.setState({
isEditing: false,
});
Expand Down Expand Up @@ -89,7 +93,7 @@ class BaseVector<T extends Vector3 | Vector6> extends React.PureComponent<BasePr
return vector;
}

handleFocus = () => {
handleFocus = (_event: React.FocusEvent<HTMLInputElement>) => {
this.setState({
isEditing: true,
text: this.getText(this.props.value),
Expand Down Expand Up @@ -127,7 +131,6 @@ class BaseVector<T extends Vector3 | Vector6> extends React.PureComponent<BasePr

return (
<InputComponent
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
Expand All @@ -136,6 +139,7 @@ class BaseVector<T extends Vector3 | Vector6> extends React.PureComponent<BasePr
autoSize ? { ...style, width: this.getText(this.state.text).length * 8 + 25 } : style
}
{...props}
isTextArea={false}
/>
);
}
Expand All @@ -147,7 +151,7 @@ export class Vector3Input extends BaseVector<Vector3> {
export class Vector6Input extends BaseVector<Vector6> {
defaultValue: Vector6 = [0, 0, 0, 0, 0, 0];
}
type BoundingBoxInputProps = Omit<InputComponentProps, "value"> & {
type BoundingBoxInputProps = Omit<InputComponentCommonProps & InputElementProps, "value"> & {
value: ServerBoundingBoxTypeTuple;
onChange: (arg0: ServerBoundingBoxTypeTuple) => void;
};
Expand Down Expand Up @@ -179,7 +183,6 @@ export class BoundingBoxInput extends React.PureComponent<BoundingBoxInputProps>
{...props}
value={vector6Value}
changeOnlyOnBlur
// @ts-expect-error ts-migrate(2322) FIXME: Type '([x, y, z, width, height, depth]: [any, any,... Remove this comment to see the full error message
onChange={([x, y, z, width, height, depth]) =>
onChange({
topLeft: [x, y, z],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ class DatasetPositionView extends PureComponent<Props> {
</Tooltip>
<Vector3Input
value={position}
// @ts-expect-error ts-migrate(2322) FIXME: Type '(position: Vector3) => void' is not assignab... Remove this comment to see the full error message
onChange={this.handleChangePosition}
autoSize
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ textAlign: string; }' is not assignable to... Remove this comment to see the full error message
Expand Down Expand Up @@ -159,7 +158,6 @@ class DatasetPositionView extends PureComponent<Props> {
</Tooltip>
<Vector3Input
value={rotation}
// @ts-expect-error ts-migrate(2322) FIXME: Type '(rotation: Vector3) => void' is not assignab... Remove this comment to see the full error message
onChange={this.handleChangeRotation}
style={{
textAlign: "center",
Expand Down
98 changes: 45 additions & 53 deletions frontend/javascripts/oxalis/view/components/editable_text_label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ class EditableTextLabel extends React.PureComponent<EditableTextLabelProp, State
}
}

handleInputChange = (event: React.SyntheticEvent) => {
handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
this.setState({
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'EventTarg... Remove this comment to see the full error message
value: event.target.value,
});
};
Expand Down Expand Up @@ -125,14 +124,12 @@ class EditableTextLabel extends React.PureComponent<EditableTextLabelProp, State
},
size: "small",
autoFocus: true,
// @ts-ignore
rows: this.props.rows,
};
const isInvalidStyleMaybe = this.props.isInvalid ? { color: "var(--ant-error)" } : {};

if (this.state.isEditing) {
return (
<span>
<span style={{ display: "inline-flex", alignItems: "center" }}>
{this.props.rows === 1 ? (
<React.Fragment>
<Input {...inputComponentProps} onBlur={() => this.handleOnChange} />
Expand All @@ -159,56 +156,51 @@ class EditableTextLabel extends React.PureComponent<EditableTextLabelProp, State
);
} else {
return (
/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message */
<span className={this.props.markdown ? "flex-container" : null}>
<span
style={{
margin,
display: "inline-block",
}}
/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message */
className={this.props.onClick != null ? "clickable-text" : null}
onClick={this.props.onClick}
onContextMenu={this.props.onContextMenu}
>
{this.props.markdown ? (
<Markdown
className="flex-item"
source={this.props.value}
options={{
html: false,
breaks: true,
linkify: true,
<div
style={{
margin,
display: "inline-flex",
alignItems: "center",
}}
className={this.props.onClick != null ? "clickable-text" : undefined}
onClick={this.props.onClick}
onContextMenu={this.props.onContextMenu}
>
{this.props.markdown ? (
<Markdown
className="flex-item"
source={this.props.value}
options={{
html: false,
breaks: true,
linkify: true,
}}
container="span"
style={isInvalidStyleMaybe}
/>
) : (
<span style={isInvalidStyleMaybe}>{this.props.value}</span>
)}
{this.props.disableEditing ? null : (
<Tooltip key="edit" title={`Edit ${this.props.label}`} placement="bottom">
<EditOutlined
className={this.props.markdown ? "flex-item" : undefined}
style={{
...iconStyle,
marginLeft: 5,
display: "inline",
whiteSpace: "nowrap",
}}
onClick={(evt) => {
evt.stopPropagation();
this.setState({
isEditing: true,
});
}}
container="span"
style={isInvalidStyleMaybe}
/>
) : (
<span style={isInvalidStyleMaybe}>{this.props.value}</span>
)}
{this.props.disableEditing ? null : (
<Tooltip key="edit" title={`Edit ${this.props.label}`} placement="bottom">
<EditOutlined
/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message */
className={this.props.markdown ? "flex-item" : null}
style={{
/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ iconStyle: { cursor: string; }; marginLeft... Remove this comment to see the full error message */
iconStyle,
marginLeft: 5,
display: "inline",
whiteSpace: "nowrap",
}}
onClick={(evt) => {
evt.stopPropagation();
this.setState({
isEditing: true,
});
}}
/>
</Tooltip>
)}
</span>
</span>
</Tooltip>
)}
</div>
);
}
}
Expand Down
54 changes: 44 additions & 10 deletions frontend/javascripts/oxalis/view/components/input_component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,36 @@ import * as React from "react";
import _ from "lodash";
import TextArea, { TextAreaProps } from "antd/lib/input/TextArea";

export type InputComponentProps = InputProps &
TextAreaProps & {
title?: React.ReactNode;
isTextArea?: boolean;
};
export type InputComponentProps = InputComponentCommonProps &
(InputElementProps | TextAreaElementProps);

export type InputComponentCommonProps = {
value: React.InputHTMLAttributes<HTMLInputElement>["value"];
title?: React.ReactNode;
style?: React.InputHTMLAttributes<HTMLInputElement>["style"];
placeholder?: React.InputHTMLAttributes<HTMLInputElement>["placeholder"];
disabled?: React.InputHTMLAttributes<HTMLInputElement>["disabled"];
size?: InputProps["size"];
};

export type InputElementProps = {
// The discriminated union
isTextArea?: false;
onChange?: React.ChangeEventHandler<HTMLInputElement>;
onBlur?: React.FocusEventHandler<HTMLInputElement>;
onFocus?: React.FocusEventHandler<HTMLInputElement>;
onPressEnter?: React.KeyboardEventHandler<HTMLInputElement>;
};

type TextAreaElementProps = {
isTextArea: true;
autoSize: TextAreaProps["autoSize"];
rows: TextAreaProps["rows"];
onChange?: React.ChangeEventHandler<HTMLTextAreaElement>;
onBlur?: React.FocusEventHandler<HTMLTextAreaElement>;
onFocus?: React.FocusEventHandler<HTMLTextAreaElement>;
onPressEnter?: React.KeyboardEventHandler<HTMLTextAreaElement>;
};

type InputComponentState = {
isFocused: boolean;
Expand All @@ -26,8 +51,6 @@ type InputComponentState = {
class InputComponent extends React.PureComponent<InputComponentProps, InputComponentState> {
static defaultProps: InputComponentProps = {
onChange: _.noop,
onFocus: _.noop,
onBlur: _.noop,
onPressEnter: undefined,
placeholder: "",
value: "",
Expand Down Expand Up @@ -96,9 +119,19 @@ class InputComponent extends React.PureComponent<InputComponentProps, InputCompo

render() {
const { isTextArea, onPressEnter, title, style, ...inputProps } = this.props;
const InputComponentType: typeof TextArea | typeof Input = isTextArea ? TextArea : Input;
const input = (
<InputComponentType
const input = isTextArea ? (
<TextArea
{...inputProps}
style={title == null ? style : undefined}
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
value={this.state.currentValue}
onPressEnter={onPressEnter}
onKeyDown={this.blurOnEscape}
/>
) : (
<Input
{...inputProps}
style={title == null ? style : undefined}
onChange={this.handleChange}
Expand All @@ -109,6 +142,7 @@ class InputComponent extends React.PureComponent<InputComponentProps, InputCompo
onKeyDown={this.blurOnEscape}
/>
);

// The input needs to be wrapped in a span in order for the tooltip to work. See https://github.com/react-component/tooltip/issues/18#issuecomment-140078802.
return title != null ? (
<Tooltip title={title} style={style}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function MarkdownModal({
label: string;
isOpen?: boolean;
onOk: () => void;
onChange: (arg0: React.SyntheticEvent) => void;
onChange: React.ChangeEventHandler<HTMLTextAreaElement>;
}) {
return (
<Modal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,10 @@ class CommentTabView extends React.Component<PropsWithSkeleton, CommentTabState>
this.nextComment(false);
};

handleChangeInput = (evt: React.SyntheticEvent, insertLineBreaks: boolean = false) => {
handleChangeInput = (
evt: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
insertLineBreaks: boolean = false,
) => {
// @ts-ignore
const commentText = evt.target.value;

Expand Down Expand Up @@ -456,8 +459,12 @@ class CommentTabView extends React.Component<PropsWithSkeleton, CommentTabState>
? undefined
: messages["tracing.read_only_mode_notification"]
}
onChange={(evt) => this.handleChangeInput(evt, true)}
onPressEnter={(evt) => (evt.target as HTMLElement).blur()}
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
this.handleChangeInput(evt, true)
}
onPressEnter={(evt: React.KeyboardEvent<HTMLInputElement>) =>
(evt.target as HTMLElement).blur()
}
placeholder="Add comment"
style={{
width: "50%",
Expand Down
Loading

0 comments on commit 8a996ac

Please sign in to comment.