React UI: Changing color for a shape (#1194)

* Minimized size of an element in side panel

* To background / to foreground like in legacy UI

* Added color changer for a shape

* Adjusted color updating
main
Boris Sekachev 6 years ago committed by GitHub
parent 93d57131c3
commit 90d594d706
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -464,9 +464,10 @@ export class CanvasViewImpl implements CanvasView, Listener {
const circle: SVG.Circle = this.nested
.circle(this.options.pointSize)
.stroke('black')
.fill(shape.node.getAttribute('fill') || 'inherit')
.fill('inherit')
.center(cx, cy)
.attr({
'fill-opacity': 1,
'stroke-width': consts.POINTS_STROKE_WIDTH / self.geometry.scale,
});
@ -496,6 +497,11 @@ export class CanvasViewImpl implements CanvasView, Listener {
deepSelect: true,
});
}
const handler = shape.remember('_selectHandler');
if (handler && handler.nested) {
handler.nested.fill(shape.attr('fill'));
}
}
public constructor(model: CanvasModel & Master, controller: CanvasController) {
@ -1314,10 +1320,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
.addClass('cvat_canvas_shape').attr({
clientID: state.clientID,
id: `cvat_canvas_shape_${state.clientID}`,
fill: state.color,
'data-z-order': state.zOrder,
}).style({
'fill-opacity': 1,
});
group.bbox = basicPolyline.bbox.bind(basicPolyline);

@ -345,7 +345,7 @@
if (updated.color) {
checkObjectType('color', data.color, 'string', null);
if (/^#[0-9A-F]{6}$/i.test(data.color)) {
if (!/^#[0-9A-F]{6}$/i.test(data.color)) {
throw new ArgumentError(
`Got invalid color value: "${data.color}"`,
);

@ -302,15 +302,12 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
// TODO: In this approach CVAT-UI know details of implementations CVAT-CANVAS (svg.js)
const shapeView = window.document.getElementById(`cvat_canvas_shape_${state.clientID}`);
if (shapeView) {
if (['rect', 'polygon', 'polyline'].includes(shapeView.tagName)) {
(shapeView as any).instance.fill({ color: shapeColor, opacity: opacity / 100 });
(shapeView as any).instance.stroke({ color: blackBorders ? 'black' : shapeColor });
} else {
// group of points
for (const child of (shapeView as any).instance.children()) {
child.fill({ color: shapeColor });
}
const handler = (shapeView as any).instance.remember('_selectHandler');
if (handler && handler.nested) {
handler.nested.fill({ color: shapeColor });
}
(shapeView as any).instance.fill({ color: shapeColor, opacity: opacity / 100 });
(shapeView as any).instance.stroke({ color: blackBorders ? 'black' : shapeColor });
}
}
}

@ -0,0 +1,56 @@
import React from 'react';
import {
Row,
Col,
Button,
} from 'antd';
interface Props {
colors: string[];
onChange(color: string): void;
}
function ColorChanger(props: Props): JSX.Element {
const {
colors,
onChange,
} = props;
const cols = 6;
const rows = Math.ceil(colors.length / cols);
const antdRows = [];
for (let row = 0; row < rows; row++) {
const antdCols = [];
for (let col = 0; col < cols; col++) {
const idx = row * cols + col;
if (idx >= colors.length) {
break;
}
const color = colors[idx];
antdCols.push(
<Col key={col} span={4}>
<Button
onClick={(): void => onChange(color)}
style={{ background: color }}
className='cvat-label-item-color-button'
/>
</Col>,
);
}
antdRows.push(
// eslint-disable-next-line react/no-children-prop
<Row key={row} children={antdCols} />,
);
}
return (
<>
{antdRows}
</>
);
}
export default React.memo(ColorChanger);

@ -9,53 +9,7 @@ import {
} from 'antd';
import Text from 'antd/lib/typography/Text';
interface PopoverContentProps {
colors: string[];
changeColor(color: string): void;
}
function PopoverContent(props: PopoverContentProps): JSX.Element {
const {
colors,
changeColor,
} = props;
const cols = 6;
const rows = Math.ceil(colors.length / cols);
const antdRows = [];
for (let row = 0; row < rows; row++) {
const antdCols = [];
for (let col = 0; col < cols; col++) {
const idx = row * cols + col;
if (idx >= colors.length) {
break;
}
const color = colors[idx];
antdCols.push(
<Col key={col} span={4}>
<Button
onClick={(): void => changeColor(color)}
style={{ background: color }}
className='cvat-label-item-color-button'
/>
</Col>,
);
}
antdRows.push(
// eslint-disable-next-line react/no-children-prop
<Row key={row} children={antdCols} />,
);
}
return (
<>
{antdRows}
</>
);
}
import ColorChanger from 'components/annotation-page/standard-workspace/objects-side-bar/color-changer';
interface Props {
labelName: string;
@ -99,8 +53,8 @@ function LabelItemComponent(props: Props): JSX.Element {
placement='left'
trigger='click'
content={(
<PopoverContent
changeColor={changeColor}
<ColorChanger
onChange={changeColor}
colors={labelColors}
/>
)}

@ -14,11 +14,13 @@ import {
Menu,
Button,
Modal,
Popover,
} from 'antd';
import Text from 'antd/lib/typography/Text';
import { RadioChangeEvent } from 'antd/lib/radio';
import { CheckboxChangeEvent } from 'antd/lib/checkbox';
import ColorChanger from 'components/annotation-page/standard-workspace/objects-side-bar/color-changer';
import {
ObjectOutsideIcon,
@ -134,12 +136,12 @@ function ItemTopComponent(props: ItemTopComponentProps): JSX.Element {
return (
<Row type='flex' align='middle'>
<Col span={10}>
<Text style={{ fontSize: 16 }}>{clientID}</Text>
<Text style={{ fontSize: 12 }}>{clientID}</Text>
<br />
<Text style={{ fontSize: 10 }}>{type}</Text>
<Text type='secondary' style={{ fontSize: 10 }}>{type}</Text>
</Col>
<Col span={12}>
<Select value={`${labelID}`} onChange={changeLabel}>
<Select size='small' value={`${labelID}`} onChange={changeLabel}>
{ labels.map((label: any): JSX.Element => (
<Select.Option key={label.id} value={`${label.id}`}>
{label.name}
@ -349,7 +351,7 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
changeAttribute(attrID, value);
}}
>
<Text strong className='cvat-text' style={{ fontSize: '1.2em' }}>
<Text strong className='cvat-text'>
{attrName}
</Text>
</Checkbox>
@ -365,6 +367,7 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
<Text strong className='cvat-text'>{attrName}</Text>
</legend>
<Radio.Group
size='small'
value={attrValue}
onChange={(event: RadioChangeEvent): void => {
changeAttribute(attrID, event.target.value);
@ -383,12 +386,13 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
return (
<>
<Col span={24}>
<Text strong className='cvat-text' style={{ fontSize: '1.2em' }}>
<Text strong className='cvat-text'>
{attrName}
</Text>
</Col>
<Col span={24}>
<Select
size='small'
onChange={(value: string): void => {
changeAttribute(attrID, value);
}}
@ -410,12 +414,13 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
return (
<>
<Col span={24}>
<Text strong className='cvat-text' style={{ fontSize: '1.2em' }}>
<Text strong className='cvat-text'>
{attrName}
</Text>
</Col>
<Col span={24}>
<InputNumber
size='small'
onChange={(value: number | undefined): void => {
if (typeof (value) !== 'undefined') {
changeAttribute(attrID, `${value}`);
@ -435,12 +440,13 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
return (
<>
<Col span={24}>
<Text strong className='cvat-text' style={{ fontSize: '1.2em' }}>
<Text strong className='cvat-text'>
{attrName}
</Text>
</Col>
<Col span={24}>
<Input
size='small'
onChange={(event: React.ChangeEvent<HTMLInputElement>): void => {
changeAttribute(attrID, event.target.value);
}}
@ -501,7 +507,7 @@ function ItemAttributesComponent(props: ItemAttributesComponentProps): JSX.Eleme
onChange={collapse}
>
<Collapse.Panel
header='Details'
header={<span style={{ fontSize: '11px' }}>Details</span>}
key='details'
>
{ sorted.map((attribute: any): JSX.Element => (
@ -544,6 +550,7 @@ interface Props {
keyframe: boolean | undefined;
attrValues: Record<number, string>;
color: string;
colors: string[];
labels: any[];
attributes: any[];
@ -572,6 +579,7 @@ interface Props {
show(): void;
changeLabel(labelID: string): void;
changeAttribute(attrID: number, value: string): void;
changeColor(color: string): void;
collapse(): void;
}
@ -613,6 +621,7 @@ function ObjectItemComponent(props: Props): JSX.Element {
attrValues,
labelID,
color,
colors,
attributes,
labels,
@ -641,6 +650,7 @@ function ObjectItemComponent(props: Props): JSX.Element {
show,
changeLabel,
changeAttribute,
changeColor,
collapse,
} = props;
@ -651,59 +661,77 @@ function ObjectItemComponent(props: Props): JSX.Element {
: 'cvat-objects-sidebar-state-item cvat-objects-sidebar-state-active-item';
return (
<div
onMouseEnter={activate}
id={`cvat-objects-sidebar-state-item-${clientID}`}
className={className}
style={{ borderLeftStyle: 'solid', borderColor: ` ${color}` }}
>
<ItemTop
serverID={serverID}
clientID={clientID}
labelID={labelID}
labels={labels}
type={type}
locked={locked}
changeLabel={changeLabel}
copy={copy}
remove={remove}
propagate={propagate}
createURL={createURL}
toBackground={toBackground}
toForeground={toForeground}
/>
<ItemButtons
objectType={objectType}
occluded={occluded}
outside={outside}
locked={locked}
hidden={hidden}
keyframe={keyframe}
navigateFirstKeyframe={navigateFirstKeyframe}
navigatePrevKeyframe={navigatePrevKeyframe}
navigateNextKeyframe={navigateNextKeyframe}
navigateLastKeyframe={navigateLastKeyframe}
setOccluded={setOccluded}
unsetOccluded={unsetOccluded}
setOutside={setOutside}
unsetOutside={unsetOutside}
setKeyframe={setKeyframe}
unsetKeyframe={unsetKeyframe}
lock={lock}
unlock={unlock}
hide={hide}
show={show}
/>
{ !!attributes.length
&& (
<ItemAttributes
collapsed={collapsed}
attributes={attributes}
values={attrValues}
collapse={collapse}
changeAttribute={changeAttribute}
<div style={{ display: 'flex' }}>
<Popover
placement='left'
trigger='click'
content={(
<ColorChanger
onChange={changeColor}
colors={colors}
/>
)}
>
<div
className='cvat-objects-sidebar-state-item-color'
style={{ background: ` ${color}` }}
/>
</Popover>
<div
onMouseEnter={activate}
id={`cvat-objects-sidebar-state-item-${clientID}`}
className={className}
style={{ borderColor: ` ${color}` }}
>
<ItemTop
serverID={serverID}
clientID={clientID}
labelID={labelID}
labels={labels}
type={type}
locked={locked}
changeLabel={changeLabel}
copy={copy}
remove={remove}
propagate={propagate}
createURL={createURL}
toBackground={toBackground}
toForeground={toForeground}
/>
<ItemButtons
objectType={objectType}
occluded={occluded}
outside={outside}
locked={locked}
hidden={hidden}
keyframe={keyframe}
navigateFirstKeyframe={navigateFirstKeyframe}
navigatePrevKeyframe={navigatePrevKeyframe}
navigateNextKeyframe={navigateNextKeyframe}
navigateLastKeyframe={navigateLastKeyframe}
setOccluded={setOccluded}
unsetOccluded={unsetOccluded}
setOutside={setOutside}
unsetOutside={unsetOutside}
setKeyframe={setKeyframe}
unsetKeyframe={unsetKeyframe}
lock={lock}
unlock={unlock}
hide={hide}
show={show}
/>
{ !!attributes.length
&& (
<ItemAttributes
collapsed={collapsed}
attributes={attributes}
values={attrValues}
collapse={collapse}
changeAttribute={changeAttribute}
/>
)}
</div>
</div>
);
}

@ -27,7 +27,7 @@
fill: $objects-bar-icons-color;
color: $objects-bar-icons-color;
font-size: 21px;
font-size: 15px;
&:hover {
transform: scale(1.1);
@ -114,13 +114,25 @@
background: $active-object-item-background-color;
}
.cvat-objects-sidebar-state-item-color {
width: 7px;
opacity: 1;
&:hover {
opacity: 0.7;
}
}
.cvat-objects-sidebar-state-item {
width: 100%;
padding: 5px 5px 5px 5px;
border-left-width: 5px;
padding: 5px 3px 3px 3px;
border-bottom: 1px dashed;
> div:nth-child(1) {
> div:nth-child(1) {
line-height: 12px;
}
> div:nth-child(3) > i {
@extend .cvat-object-sidebar-icon;
font-size: 25px;
@ -133,17 +145,13 @@
> div:nth-child(2) {
> div > div {
margin-top: 10px;
margin-top: 5px;
}
i {
@extend .cvat-object-sidebar-icon;
}
}
> div:nth-child(3) {
margin-top: 10px;
}
}
.cvat-objects-sidebar-state-item-collapse {
@ -193,7 +201,7 @@
max-width: 80%;
overflow: hidden;
max-height: 1.2em;
font-size: 1.2em;
font-size: 1em;
> span {
padding: 0 10px;

@ -32,6 +32,7 @@ interface StateToProps {
activated: boolean;
colorBy: ColorBy;
ready: boolean;
colors: string[];
activeControl: ActiveControl;
minZLayer: number;
maxZLayer: number;
@ -73,6 +74,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
ready,
activeControl,
},
colors,
},
settings: {
shapes: {
@ -96,6 +98,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
ready,
activeControl,
colorBy,
colors,
jobInstance,
frameNumber,
activated: activatedStateID === own.clientID,
@ -234,10 +237,8 @@ class ObjectItemContainer extends React.PureComponent<Props> {
minZLayer,
} = this.props;
if (objectState.zOrder !== minZLayer) {
objectState.zOrder = minZLayer - 1;
this.commit();
}
objectState.zOrder = minZLayer - 1;
this.commit();
};
private toForeground = (): void => {
@ -246,10 +247,8 @@ class ObjectItemContainer extends React.PureComponent<Props> {
maxZLayer,
} = this.props;
if (objectState.zOrder !== maxZLayer) {
objectState.zOrder = maxZLayer + 1;
this.commit();
}
objectState.zOrder = maxZLayer + 1;
this.commit();
};
private activate = (): void => {
@ -335,6 +334,15 @@ class ObjectItemContainer extends React.PureComponent<Props> {
collapseOrExpand([objectState], !collapsed);
};
private changeColor = (color: string): void => {
const {
objectState,
} = this.props;
objectState.color = color;
this.commit();
};
private changeLabel = (labelID: string): void => {
const {
objectState,
@ -374,6 +382,7 @@ class ObjectItemContainer extends React.PureComponent<Props> {
frameNumber,
activated,
colorBy,
colors,
} = this.props;
const {
@ -412,6 +421,7 @@ class ObjectItemContainer extends React.PureComponent<Props> {
attrValues={{ ...objectState.attributes }}
labelID={objectState.label.id}
color={stateColor}
colors={colors}
attributes={attributes}
labels={labels}
collapsed={collapsed}
@ -448,6 +458,7 @@ class ObjectItemContainer extends React.PureComponent<Props> {
unlock={this.unlock}
hide={this.hide}
show={this.show}
changeColor={this.changeColor}
changeLabel={this.changeLabel}
changeAttribute={this.changeAttribute}
collapse={this.collapse}

Loading…
Cancel
Save