You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

331 lines
10 KiB
TypeScript

import React from 'react';
import {
Tabs,
Icon,
Button,
Tooltip,
notification,
} from 'antd';
import Text from 'antd/lib/typography/Text';
import copy from 'copy-to-clipboard';
import RawViewer from './raw-viewer';
import ConstructorViewer from './constructor-viewer';
import ConstructorCreator from './constructor-creator';
import ConstructorUpdater from './constructor-updater';
import {
idGenerator,
Label,
Attribute,
} from './common';
enum ConstructorMode {
SHOW = 'SHOW',
CREATE = 'CREATE',
UPDATE = 'UPDATE',
}
interface LabelsEditortProps {
labels: Label[];
onSubmit: (labels: any[]) => void;
}
interface LabelsEditorState {
constructorMode: ConstructorMode;
savedLabels: Label[];
unsavedLabels: Label[];
labelForUpdate: Label | null;
}
export default class LabelsEditor
extends React.PureComponent<LabelsEditortProps, LabelsEditorState> {
public constructor(props: LabelsEditortProps) {
super(props);
this.state = {
savedLabels: [],
unsavedLabels: [],
constructorMode: ConstructorMode.SHOW,
labelForUpdate: null,
};
}
public componentDidMount(): void {
// just need performe the same code
this.componentDidUpdate(null as any as LabelsEditortProps);
}
public componentDidUpdate(prevProps: LabelsEditortProps): void {
function transformLabel(label: any): Label {
return {
name: label.name,
id: label.id || idGenerator(),
attributes: label.attributes.map((attr: any): Attribute => (
{
id: attr.id || idGenerator(),
name: attr.name,
type: attr.input_type,
mutable: attr.mutable,
values: [...attr.values],
}
)),
};
}
const { labels } = this.props;
if (!prevProps || prevProps.labels !== labels) {
const transformedLabels = labels.map(transformLabel);
this.setState({
savedLabels: transformedLabels
.filter((label: Label) => label.id >= 0),
unsavedLabels: transformedLabels
.filter((label: Label) => label.id < 0),
});
}
}
private handleRawSubmit = (labels: Label[]): void => {
const unsavedLabels = [];
const savedLabels = [];
for (const label of labels) {
if (label.id >= 0) {
savedLabels.push(label);
} else {
unsavedLabels.push(label);
}
}
this.setState({
unsavedLabels,
savedLabels,
});
this.handleSubmit(savedLabels, unsavedLabels);
};
private handleCreate = (label: Label | null): void => {
if (label === null) {
this.setState({
constructorMode: ConstructorMode.SHOW,
});
} else {
const {
unsavedLabels,
savedLabels,
} = this.state;
const newUnsavedLabels = [
...unsavedLabels,
{
...label,
id: idGenerator(),
},
];
this.setState({
unsavedLabels: newUnsavedLabels,
});
this.handleSubmit(savedLabels, newUnsavedLabels);
}
};
private handleUpdate = (label: Label | null): void => {
const {
savedLabels,
unsavedLabels,
} = this.state;
if (label) {
const filteredSavedLabels = savedLabels
.filter((_label: Label) => _label.id !== label.id);
const filteredUnsavedLabels = unsavedLabels
.filter((_label: Label) => _label.id !== label.id);
if (label.id >= 0) {
filteredSavedLabels.push(label);
this.setState({
savedLabels: filteredSavedLabels,
constructorMode: ConstructorMode.SHOW,
});
} else {
filteredUnsavedLabels.push(label);
this.setState({
unsavedLabels: filteredUnsavedLabels,
constructorMode: ConstructorMode.SHOW,
});
}
this.handleSubmit(filteredSavedLabels, filteredUnsavedLabels);
} else {
this.setState({
constructorMode: ConstructorMode.SHOW,
});
}
};
private handleDelete = (label: Label): void => {
// the label is saved on the server, cannot delete it
if (typeof (label.id) !== 'undefined' && label.id >= 0) {
notification.error({
message: 'Could not delete the label',
description: 'It has been already saved on the server',
});
}
const {
unsavedLabels,
savedLabels,
} = this.state;
const filteredUnsavedLabels = unsavedLabels.filter(
(_label: Label): boolean => _label.id !== label.id,
);
this.setState({
unsavedLabels: filteredUnsavedLabels,
});
this.handleSubmit(savedLabels, filteredUnsavedLabels);
};
private handleSubmit(savedLabels: Label[], unsavedLabels: Label[]): void {
function transformLabel(label: Label): any {
return {
name: label.name,
id: label.id < 0 ? undefined : label.id,
attributes: label.attributes.map((attr: Attribute): any => (
{
name: attr.name,
id: attr.id < 0 ? undefined : attr.id,
input_type: attr.type.toLowerCase(),
default_value: attr.values[0],
mutable: attr.mutable,
values: [...attr.values],
}
)),
};
}
const { onSubmit } = this.props;
const output = [];
for (const label of savedLabels.concat(unsavedLabels)) {
output.push(transformLabel(label));
}
onSubmit(output);
}
public render(): JSX.Element {
const {
savedLabels,
unsavedLabels,
constructorMode,
labelForUpdate,
} = this.state;
return (
<Tabs
defaultActiveKey='2'
type='card'
tabBarStyle={{ marginBottom: '0px' }}
tabBarExtraContent={(
<>
<Tooltip overlay='Copied to clipboard!' trigger='click'>
<Button
type='link'
icon='copy'
onClick={(): void => {
copy(JSON.stringify(
savedLabels.concat(unsavedLabels).map((label): any => ({
...label,
id: undefined,
attributes: label.attributes.map((attribute): any => ({
...attribute,
id: undefined,
})),
})), null, 4,
));
}}
>
Copy
</Button>
</Tooltip>
</>
)}
>
<Tabs.TabPane
tab={
(
<span>
<Icon type='edit' />
<Text>Raw</Text>
</span>
)
}
key='1'
>
<RawViewer
labels={[...savedLabels, ...unsavedLabels]}
onSubmit={this.handleRawSubmit}
/>
</Tabs.TabPane>
<Tabs.TabPane
tab={
(
<span>
<Icon type='build' />
<Text>Constructor</Text>
</span>
)
}
key='2'
>
{
constructorMode === ConstructorMode.SHOW
&& (
<ConstructorViewer
labels={[...savedLabels, ...unsavedLabels]}
onUpdate={(label: Label): void => {
this.setState({
constructorMode: ConstructorMode.UPDATE,
labelForUpdate: label,
});
}}
onDelete={this.handleDelete}
onCreate={(): void => {
this.setState({
constructorMode: ConstructorMode.CREATE,
});
}}
/>
)
}
{
constructorMode === ConstructorMode.UPDATE
&& labelForUpdate !== null && (
<ConstructorUpdater
label={labelForUpdate}
onUpdate={this.handleUpdate}
/>
)
}
{
constructorMode === ConstructorMode.CREATE
&& (
<ConstructorCreator
onCreate={this.handleCreate}
/>
)
}
</Tabs.TabPane>
</Tabs>
);
}
}