lost-sia
Version:
Single Image Annotation Tool
239 lines (202 loc) • 4.92 kB
JSX
import React, { useState, useEffect, useRef } from "react";
import { Dropdown, Ref, Popup, Header } from "semantic-ui-react";
const LabelInput = ({
defaultLabel,
disabled,
focusOnRender,
initLabelIds,
multilabels,
possibleLabelsProp,
relatedId,
renderPopup,
visible,
open,
onClose,
onLabelConfirmed,
onLabelUpdate,
}) => {
const [label, setLabel] = useState([]);
const [possibleLabels, setPossibleLabels] = useState([]);
const [performInit, setPerformInit] = useState(true);
const [confirmLabel, setConfirmLabel] = useState(0);
const inputRef = useRef(null);
useEffect(() => {
updatePossibleLabels();
}, []);
useEffect(() => {
if (initLabelIds) {
setPerformInit(true);
}
}, [initLabelIds]);
useEffect(() => {
if (visible) {
if (focusOnRender) {
if (inputRef.current) {
inputRef.current.click();
}
}
}
if (confirmLabel !== 0) {
annoLabelUpdate(label);
closeLabelInput();
}
if (initLabelIds) {
if (performInit) {
setPerformInit(false);
if (initLabelIds.length > 0) {
setLabel(initLabelIds);
} else {
setLabel([]);
}
}
}
}, [
visible,
focusOnRender,
label,
confirmLabel,
possibleLabelsProp,
initLabelIds,
relatedId,
]);
const onKeyDown = (e) => {
e.stopPropagation();
performKeyAction(e.key);
};
const onChange = (e, item) => {
let lbl;
if (multilabels) {
lbl = item.value !== -1 ? item.value : [];
} else {
lbl = item.value !== -1 ? [item.value] : [];
}
setLabel(lbl);
annoLabelUpdate(lbl);
};
const onItemClick = (e, item) => {
incrementConfirmLabel();
};
const updatePossibleLabels = () => {
let _possibleLabels = [];
let _defaultLabel;
if (defaultLabel) {
if (Number.isInteger(defaultLabel)) {
_defaultLabel = undefined;
} else {
_defaultLabel = defaultLabel;
}
} else {
_defaultLabel = "no label";
}
if (possibleLabelsProp.length > 0) {
_possibleLabels = possibleLabelsProp.map((e) => {
return {
key: e.id,
value: e.id,
text: e.label,
content: (
<div onClick={(event) => onItemClick(event, e.id)}>{e.label}</div>
),
};
});
}
if (_defaultLabel) {
_possibleLabels.unshift({
key: -1,
value: -1,
text: _defaultLabel,
content: (
<div onClick={(event) => onItemClick(event, -1)}>{_defaultLabel}</div>
),
});
}
setPossibleLabels(_possibleLabels);
};
const performKeyAction = (key) => {
switch (key) {
case "Enter":
if (!multilabels) {
if (visible) incrementConfirmLabel();
}
break;
case "Escape":
closeLabelInput();
break;
default:
break;
}
};
const annoLabelUpdate = (label) => {
// console.log("LabelInput -> annoLabelUpdate ", label);
if (onLabelUpdate) {
onLabelUpdate(label.filter((val) => val !== -1));
}
};
const incrementConfirmLabel = () => {
setConfirmLabel(confirmLabel + 1);
};
const closeLabelInput = () => {
// console.log("LabelInput -> closeLabelInput");
if (onLabelConfirmed) onLabelConfirmed(label.filter((val) => val !== -1));
if (onClose) onClose();
};
const renderLabelInput = () => {
let lbl;
if (multilabels) lbl = label;
else {
lbl = label.length > 0 ? label[0] : -1;
}
return (
<Ref innerRef={inputRef}>
<Dropdown
multiple={multilabels}
search
selection
closeOnChange
icon="search"
options={possibleLabels}
placeholder="Enter label"
tabIndex={0}
onKeyDown={(e) => onKeyDown(e)}
value={lbl}
onChange={(e, item) => onChange(e, item)}
style={{ opacity: 0.8 }}
disabled={disabled}
open={open}
/>
</Ref>
);
};
const renderLabelInfo = () => {
if (!label) return null;
let lbl = undefined;
if (label.length > 0) {
lbl = possibleLabels.find((e) => label[label.length - 1] === e.id);
}
if (!lbl) return "No label";
return (
<div>
<Header>{lbl.label}</Header>
<div dangerouslySetInnerHTML={{ __html: lbl.description }} />
</div>
);
};
const renderPopupContent = () => {
return <div>{renderLabelInfo()}</div>;
};
if (!visible) return null;
if (renderPopup) {
return (
<Popup
trigger={renderLabelInput()}
content={renderPopupContent()}
open
position="right center"
style={{ opacity: 0.9 }}
/>
);
} else {
return renderLabelInput();
}
};
export default LabelInput;