Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.

Commit a45fc8c

Browse files
hlomzikbmartelnick-skriabin
authored
feat: DEV-3391: Render real Annotations in View All (#928)
* DEV-3391: Duplicate root into every annotation * Add id for Choice(s) for test All other tags should have the only indentifier then * Use own names/toNames in every Annotation * Render tags from given annotation, not common * Fix tags with both name and id * Fix names for duplicated tags; fix RichText * Finally use parent Annotation, not selected * View All! Real Annotations! Much UX * All Labels related tags now have id and name: string * Fix names usage - toNames should contain references from names, not strings - Area dispatcher should check for cleaned up ids/names * Fix Labels.annotation by mixin * Add feature flag (WIP) * Full feature flag coverage * Fix id/name props for every tag Also don't add tags without id/name to ids map. * Fix Channel * Fix renderChildren, Channel and visual tags * Fix names for tool tags * Fix AnnotationMixin for Tools and AnnotationHistory * Fix Taxonomy perRegion visibility * Fix results created by current user Connect results to tags in current annotation's tags tree * Don't crash because of Relations tag It's not a real control * Other fixes for tags in area/result - correct object for new Area - fix for createDrawingRegion * Fix AnnotationMixin for Tools by @Gondragos * Fix serialization * Fix annotation access from KeyPoint Tool * fix taxonomy children * reget the children snapshots to keep it as much as it was * putting back shallow copy of children taxonomy * fix: DEV-3391: View all readonly (#964) * fix: DEV-3644: Predictions should always be selectable * fix: DEV-3644: honor readonly mode with fallbacks to editable and prediction type in snaphot creation * fix: DEV-3644: allow regions to be selectable if readonly * fix: DEV-3644: allow video regions to be selectable but non modifiable in readonly mode * only run history freeze if annotation present on initialization of richtext * improve choice spacing for selected readonly taxonomy * improve taxonomy dropdown readonly mode to be navigable * make textarea render readonly when it is not editable * compute the isEditable from textarea model * fix the topbar control state to disable readonly annotations * fix datetime and number controls readonly mode * fix image per region test * use editable state of annotation as the target for readonly/disabled states of controls * Fix taxonomy/choices loading * use isEditable for taxonomy, don't disable just block updates * use isEditable and isDeletable for TextArea controls and fix regression with deletion and edit capabilities * remove editable=true on TextArea test, this ensures regression is fixed Co-authored-by: Nick Skriabin <nr@fenelon.ru> * make Choice and TextArea disabled look if strictly readonly, block all updates if strictly non editable Co-authored-by: Brandon Martel <brandonmartel@gmail.com> Co-authored-by: Nick Skriabin <nr@fenelon.ru>
1 parent 8fd6734 commit a45fc8c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+402
-222
lines changed

src/components/App/Annotation.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ export function Annotation({ annotation, root }) {
1010
}
1111
};
1212
}, [annotation.pk, annotation.id]);
13-
return root ? Tree.renderItem(root) : null;
13+
return root ? Tree.renderItem(root, annotation) : null;
1414
}

src/components/App/Grid.js

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { observe } from "mobx";
77
import Konva from "konva";
88
import { Annotation } from "./Annotation";
99
import { isDefined } from "../../utils/utilities";
10+
import { FF_DEV_3391, isFF } from "../../utils/feature-flags";
1011
import { moveStylesBetweenHeadTags } from "../../utils/html";
1112

1213
/***** DON'T TRY THIS AT HOME *****/
@@ -51,7 +52,7 @@ export default class Grid extends Component {
5152
}
5253

5354
componentDidMount() {
54-
if (this.props.annotations[0] !== this.props.store.selected) {
55+
if (!isFF(FF_DEV_3391) && this.props.annotations[0] !== this.props.store.selected) {
5556
this.startRenderCycle();
5657
}
5758
}
@@ -151,7 +152,8 @@ export default class Grid extends Component {
151152

152153
render() {
153154
const i = this.state.item;
154-
const { annotations, store: { selected } } = this.props;
155+
const { annotations } = this.props;
156+
const selected = isFF(FF_DEV_3391) ? null : this.props.store.selected;
155157
const isRenderingNext = i < annotations.length && annotations[i] === selected;
156158

157159
return (
@@ -166,20 +168,23 @@ export default class Grid extends Component {
166168
bordered={false}
167169
style={{ height: 44 }}
168170
/>
169-
{!this.state.loaded.has(c.id) && (
170-
<div style={{
171-
top: 0,
172-
left: 0,
173-
position: 'absolute',
174-
width: '100%',
175-
height: '100%',
176-
display: 'flex',
177-
alignItems: 'center',
178-
justifyContent: 'center',
179-
}}>
180-
<Spin size="large" />
181-
</div>
182-
)}
171+
{isFF(FF_DEV_3391)
172+
? <Annotation root={this.props.root} annotation={c} />
173+
: !this.state.loaded.has(c.id) && (
174+
<div style={{
175+
top: 0,
176+
left: 0,
177+
position: 'absolute',
178+
width: '100%',
179+
height: '100%',
180+
display: 'flex',
181+
alignItems: 'center',
182+
justifyContent: 'center',
183+
}}>
184+
<Spin size="large" />
185+
</div>
186+
)
187+
}
183188
</div>
184189
))}
185190
{isRenderingNext && (

src/components/HtxTextBox/HtxTextBox.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,20 +145,20 @@ export class HtxTextBox extends React.Component {
145145
}
146146

147147
renderView() {
148-
const { onChange, onDelete, text, ...props } = this.props;
148+
const { onChange, onDelete, isEditable, isDeleteable, text, ...props } = this.props;
149149

150150
return (
151151
<>
152152
<Paragraph {...props}>
153153
<span ref={this.textRef}>{text}</span>
154-
{onChange && <EditOutlined onClick={this.startEditing} className="ant-typography-edit" />}
154+
{isEditable && onChange && <EditOutlined onClick={this.startEditing} className="ant-typography-edit" />}
155155
</Paragraph>
156-
{onDelete && <DeleteOutlined className={styles.delete} onClick={onDelete} />}
156+
{isDeleteable && onDelete && <DeleteOutlined className={styles.delete} onClick={onDelete} />}
157157
</>
158158
);
159159
}
160160

161161
render() {
162-
return this.state.editing || this.props.onlyEdit ? this.renderEdit() : this.renderView();
162+
return (this.state.editing || this.props.onlyEdit) && this.props.isEditable ? this.renderEdit() : this.renderView();
163163
}
164164
}

src/components/ImageView/ImageView.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const splitRegions = (regions) => {
5050
};
5151

5252
const Region = memo(({ region, showSelected = false }) => {
53-
return useObserver(() => region.inSelection !== showSelected ? null : Tree.renderItem(region, false));
53+
return useObserver(() => region.inSelection !== showSelected ? null : Tree.renderItem(region, region.annotation, false));
5454
});
5555

5656
const RegionsLayer = memo(({ regions, name, useLayers, showSelected = false }) => {
@@ -466,7 +466,7 @@ export default observer(
466466

467467
handleOnClick = e => {
468468
const { item } = this.props;
469-
469+
470470
if (isFF(FF_DEV_1442)) {
471471
this.handleDeferredMouseDown?.();
472472
}
@@ -475,8 +475,6 @@ export default observer(
475475
return;
476476
}
477477

478-
if (!item.annotation.editable) return;
479-
480478
const evt = e.evt || e;
481479

482480
return item.event("click", evt, evt.offsetX, evt.offsetY);

src/components/Taxonomy/Taxonomy.module.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,13 @@
3333
margin: 0 2px 4px 0;
3434
background: hsl(0, 0%, 95%);
3535
padding: 0; // all the right space should be a clickable button, so no padding
36-
padding-left: 8px;
3736
border-radius: 4px;
3837
display: flex;
3938
align-items: center;
39+
40+
span {
41+
padding-inline: 8px;
42+
}
4043
}
4144

4245
input[type="button"] {
@@ -50,6 +53,7 @@
5053
font-weight: 500;
5154
font-size: 20px;
5255
color: #09f;
56+
margin-left: -8px; // to align with the right edge of the text
5357

5458
&:hover {
5559
color: red;

src/components/Taxonomy/Taxonomy.tsx

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type TaxonomyProps = {
4343
onAddLabel?: onAddLabelCallback,
4444
onDeleteLabel?: onDeleteLabelCallback,
4545
options?: TaxonomyOptions,
46-
isReadonly?: boolean,
46+
isEditable?: boolean,
4747
};
4848

4949
type TaxonomySelectedContextValue = [TaxonomyPath[], (path: TaxonomyPath, value: boolean) => any];
@@ -61,6 +61,7 @@ interface RowProps {
6161
style: any;
6262
dimensionCallback: (ref: any) => void;
6363
maxWidth: number;
64+
isEditable?: boolean;
6465
item: {
6566
row: {
6667
id: string,
@@ -113,7 +114,7 @@ const UserLabelForm = ({ onAddLabel, onFinish, path }: UserLabelFormProps) => {
113114
);
114115
};
115116

116-
const SelectedList = ({ isReadonly, flatItems } : { isReadonly:boolean, flatItems:TaxonomyItem[] }) => {
117+
const SelectedList = ({ isEditable, flatItems } : { isEditable: boolean, flatItems:TaxonomyItem[] }) => {
117118
const [selected, setSelected] = useContext(TaxonomySelectedContext);
118119
const { showFullPath, pathSeparator = " / " } = useContext(TaxonomyOptionsContext);
119120

@@ -130,10 +131,10 @@ const SelectedList = ({ isReadonly, flatItems } : { isReadonly:boolean, flatItem
130131
<div className={styles.taxonomy__selected}>
131132
{selectedLabels.map((path, index) => (
132133
<div key={path.join("|")}>
133-
{showFullPath ? path.join(pathSeparator) : path[path.length - 1]}
134-
{!isReadonly &&
134+
<span>{showFullPath ? path.join(pathSeparator) : path[path.length - 1]}</span>
135+
{isEditable ? (
135136
<input type="button" onClick={() => setSelected(selected[index], false)} value="×" />
136-
}
137+
): null}
137138
</div>
138139
))}
139140
</div>
@@ -146,7 +147,7 @@ function isSubArray(item: string[], parent: string[]) {
146147
return parent.every((n, i) => item[i] === n);
147148
}
148149

149-
const Item: React.FC<RowProps> = ({ style, item, dimensionCallback, maxWidth }: RowProps) => {
150+
const Item: React.FC<RowProps> = ({ style, item, dimensionCallback, maxWidth, isEditable }: RowProps) => {
150151
const {
151152
row: { id, isOpen, childCount, isFiltering, name, path, padding, isLeaf },
152153
toggle,
@@ -226,12 +227,16 @@ const Item: React.FC<RowProps> = ({ style, item, dimensionCallback, maxWidth }:
226227
disabled={disabled}
227228
checked={checked}
228229
ref={setIndeterminate}
229-
onChange={e => setSelected(path, e.currentTarget.checked)}
230+
onChange={e => {
231+
if (isEditable) {
232+
setSelected(path, e.currentTarget.checked);
233+
}
234+
}}
230235
/>
231236
<label
232237
htmlFor={id}
233238
style={{ maxWidth: `${labelMaxWidth}px` }}
234-
onClick={onClick}
239+
onClick={isEditable ? onClick : undefined}
235240
title={title}
236241
className={disabled ? styles.taxonomy__collapsable : undefined}
237242
>
@@ -240,7 +245,7 @@ const Item: React.FC<RowProps> = ({ style, item, dimensionCallback, maxWidth }:
240245
{!isFiltering && (
241246
<div className={styles.taxonomy__extra}>
242247
<span className={styles.taxonomy__extra_count}>{childCount}</span>
243-
{onAddLabel && (
248+
{isEditable && onAddLabel && (
244249
<div className={styles.taxonomy__extra_actions}>
245250
<Dropdown
246251
destroyPopupOnHide // important for long interactions with huge taxonomy
@@ -284,6 +289,7 @@ type TaxonomyDropdownProps = {
284289
flatten: TaxonomyItem[],
285290
items: TaxonomyItem[],
286291
show: boolean,
292+
isEditable?: boolean,
287293
};
288294

289295
const filterTreeByPredicate = (flatten: TaxonomyItem[], predicate: (item: TaxonomyItem) => boolean) => {
@@ -324,7 +330,7 @@ const filterTreeByPredicate = (flatten: TaxonomyItem[], predicate: (item: Taxono
324330
return roots;
325331
};
326332

327-
const TaxonomyDropdown = ({ show, flatten, items, dropdownRef }: TaxonomyDropdownProps) => {
333+
const TaxonomyDropdown = ({ show, flatten, items, dropdownRef, isEditable }: TaxonomyDropdownProps) => {
328334
const inputRef = useRef<HTMLInputElement>(null);
329335
const [search, setSearch] = useState("");
330336
const predicate = (item: TaxonomyItem) => item.label.toLocaleLowerCase().includes(search);
@@ -382,6 +388,7 @@ const TaxonomyDropdown = ({ show, flatten, items, dropdownRef }: TaxonomyDropdow
382388
/>
383389
<TreeStructure
384390
items={list}
391+
isEditable={isEditable}
385392
rowComponent={Item}
386393
flatten={search !== ""}
387394
rowHeight={30}
@@ -395,11 +402,11 @@ const TaxonomyDropdown = ({ show, flatten, items, dropdownRef }: TaxonomyDropdow
395402
<div className={styles.taxonomy__add__container}>
396403
{isAdding ? (
397404
<UserLabelForm path={[]} onAddLabel={onAddLabel} onFinish={closeForm} />
398-
) : (
405+
) : isEditable ? (
399406
<div className={styles.taxonomy__add}>
400407
<button onClick={addInside}>Add</button>
401408
</div>
402-
)}
409+
): null}
403410
</div>
404411
)}
405412
</div>
@@ -413,7 +420,7 @@ const Taxonomy = ({
413420
onAddLabel,
414421
onDeleteLabel,
415422
options = {},
416-
isReadonly = false,
423+
isEditable = true,
417424
}: TaxonomyProps) => {
418425
const dropdownRef = useRef<HTMLDivElement>(null);
419426
const taxonomyRef = useRef<HTMLDivElement>(null);
@@ -517,16 +524,14 @@ const Taxonomy = ({
517524
return (
518525
<TaxonomySelectedContext.Provider value={contextValue}>
519526
<TaxonomyOptionsContext.Provider value={optionsWithMaxUsages}>
520-
<SelectedList isReadonly={isReadonly} flatItems={flatten} />
521-
{!isReadonly && (
522-
<div className={[styles.taxonomy, isOpenClassName].join(" ")} ref={taxonomyRef}>
523-
<span onClick={() => setOpen(val => !val)}>
524-
{options.placeholder || "Click to add..."}
525-
<LsChevron stroke="#09f" />
526-
</span>
527-
<TaxonomyDropdown show={isOpen} items={items} flatten={flatten} dropdownRef={dropdownRef} />
528-
</div>
529-
)}
527+
<SelectedList isEditable={isEditable} flatItems={flatten} />
528+
<div className={[styles.taxonomy, isOpenClassName].join(" ")} ref={taxonomyRef}>
529+
<span onClick={() => setOpen(val => !val)}>
530+
{options.placeholder || "Click to add..."}
531+
<LsChevron stroke="#09f" />
532+
</span>
533+
<TaxonomyDropdown show={isOpen} isEditable={isEditable} items={items} flatten={flatten} dropdownRef={dropdownRef} />
534+
</div>
530535
</TaxonomyOptionsContext.Provider>
531536
</TaxonomySelectedContext.Provider>
532537
);

src/components/TopBar/Controls.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ export const Controls = controlsInjector(observer(({ store, history, annotation
3333
const isReview = store.hasInterface("review");
3434

3535
const historySelected = isDefined(store.annotationStore.selectedHistory);
36-
const { userGenerate, sentUserGenerate, versions, results } = annotation;
36+
const { userGenerate, sentUserGenerate, versions, results, editable: annotationEditable } = annotation;
3737
const buttons = [];
3838

3939
const [isInProgress, setIsInProgress] = useState(false);
4040

4141
// const isReady = store.annotationStore.selected.objects.every(object => object.isReady === undefined || object.isReady);
42-
const disabled = store.isSubmitting || historySelected || isInProgress; // || !isReady;
42+
const disabled = !annotationEditable || store.isSubmitting || historySelected || isInProgress; // || !isReady;
4343
const submitDisabled = store.hasInterface("annotations:deny-empty") && results.length === 0;
4444

4545
const buttonHandler = useCallback(async (e, callback, tooltipMessage) => {

src/components/TopBar/TopBar.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import "./TopBar.styl";
1010
export const TopBar = observer(({ store }) => {
1111
const annotationStore = store.annotationStore;
1212
const entity = annotationStore?.selected;
13-
const isPrediction = entity?.type === 'prediction';
13+
const isPrediction = entity?.type === "prediction";
1414

1515
const isViewAll = annotationStore?.viewingAll === true;
1616

@@ -36,7 +36,7 @@ export const TopBar = observer(({ store }) => {
3636
)}
3737
{!isViewAll && store.hasInterface("controls") && (store.hasInterface("review") || !isPrediction) && (
3838
<Elem name="section" mod={{ flat: true }} style={{ width: 320, boxSizing: 'border-box' }}>
39-
<Controls annotation={entity}/>
39+
<Controls annotation={entity} />
4040
</Elem>
4141
)}
4242
</Elem>

src/components/TreeStructure/TreeStructure.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ const TreeStructure = ({
6969
maxWidth,
7070
transformationCallback,
7171
defaultExpanded,
72+
isEditable,
7273
}: {
7374
items: any[],
75+
isEditable?: boolean,
7476
rowComponent: React.FC<any>,
7577
flatten: boolean,
7678
rowHeight: number,
@@ -130,12 +132,13 @@ const TreeStructure = ({
130132
};
131133

132134
const addInside = (id?: string) => {
135+
if (!isEditable) return;
136+
133137
if (id) {
134138
setData(recursiveTreeWalker({ items, addInsideId: id }));
135139
}
136140
else setData(recursiveTreeWalker({ items }));
137141
updateHeight();
138-
139142
};
140143

141144
const Row = ({
@@ -183,7 +186,7 @@ const TreeStructure = ({
183186
}, [width]);
184187

185188
return (
186-
<RowComponent {...{ item, style, dimensionCallback, maxWidth }} />
189+
<RowComponent {...{ isEditable, item, style, dimensionCallback, maxWidth }} />
187190
);
188191
};
189192

0 commit comments

Comments
 (0)