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

fix: LEAP-33: Fixes for labels by Taxonomy #1555

Merged
merged 23 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7525970
fix: LEAP-33: Mimic labeling Taxonomy result as Label
hlomzik Sep 8, 2023
db922ba
Attempt to respect `showFullPath` in New Taxonomy
hlomzik Sep 8, 2023
5330810
Fix node type detection
hlomzik Sep 13, 2023
851c2ff
Fix labeling=true for usual taxonomy (no api)
hlomzik Sep 14, 2023
e96449e
Fix region removal upon deleting last label
hlomzik Sep 14, 2023
f3445ee
Add color to Choice for labeling
hlomzik Sep 14, 2023
716550f
Merge branch 'master' into fb-leap-33/taxonomy-labels
hlomzik Sep 14, 2023
bea2027
Use colors in taxonomy as well
hlomzik Sep 15, 2023
9298c95
Simplify styles, make global; move to .styl
hlomzik Sep 22, 2023
3398db3
typo
hlomzik Sep 22, 2023
4e64688
Merge remote-tracking branch 'origin/master' into fb-leap-33/taxonomy…
hlomzik Sep 25, 2023
d49468e
Fix last item removal for old taxonomy as well
hlomzik Sep 29, 2023
fa640a9
Allow to remove items when region is not selected
hlomzik Oct 2, 2023
a8751e0
Also fix this last item for new taxonomy
hlomzik Oct 3, 2023
c07d59f
Remove excess @todo
hlomzik Oct 3, 2023
4763635
Merge remote-tracking branch 'origin/master' into fb-leap-33/taxonomy…
hlomzik Oct 4, 2023
6953203
Reorder imports and remove unused one
hlomzik Oct 5, 2023
50602ef
Merge remote-tracking branch 'origin/master' into fb-leap-33/taxonomy…
hlomzik Oct 9, 2023
71253e8
Simplify Visibility checks
hlomzik Oct 9, 2023
65e7588
Currently revert old code and wrap new one with FF
hlomzik Oct 10, 2023
d8c8fb9
Merge branch 'master' into fb-leap-33/taxonomy-labels
hlomzik Oct 10, 2023
7f924c4
Merge remote-tracking branch 'origin/master' into fb-leap-33/taxonomy…
farioas Oct 11, 2023
6168b40
Merge remote-tracking branch 'origin/master' into fb-leap-33/taxonomy…
farioas Oct 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/NewTaxonomy/NewTaxonomy.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:global(.htx-taxonomy-item-color)
padding 4px 4px
border-radius 2px
28 changes: 23 additions & 5 deletions src/components/NewTaxonomy/NewTaxonomy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { useCallback, useEffect, useState } from 'react';

import { Tooltip } from '../../common/Tooltip/Tooltip';

import './NewTaxonomy.styl';

type TaxonomyPath = string[];
type onAddLabelCallback = (path: string[]) => any;
type onDeleteLabelCallback = (path: string[]) => any;
Expand All @@ -15,6 +17,7 @@ type TaxonomyItem = {
children?: TaxonomyItem[],
origin?: 'config' | 'user' | 'session',
hint?: string,
color?: string,
};

type AntTaxonomyItem = {
Expand Down Expand Up @@ -57,17 +60,32 @@ const convert = (
options: TaxonomyExtendedOptions,
selectedPaths: string[],
): AntTaxonomyItem[] => {
// generate string or component to be the `title` of the item
const enrich = (item: TaxonomyItem) => {
const color = (item: TaxonomyItem) => (
// no BEM here to make it more lightweight
// global classname to allow to change it in Style tag
<span className="htx-taxonomy-item-color" style={{ background: item.color }}>
{item.label}
</span>
);

if (!item.hint) return item.color ? color(item) : item.label;

return (
<Tooltip title={item.hint} mouseEnterDelay={500}>
{item.color ? color(item) : <span>{item.label}</span>}
</Tooltip>
);
};

const convertItem = (item: TaxonomyItem): AntTaxonomyItem => {
const value = item.path.join(options.pathSeparator);
const disabledNode = options.leafsOnly && (item.isLeaf === false || !!item.children);
const maxUsagesReached = options.maxUsagesReached && !selectedPaths.includes(value);

return {
title: item.hint ? (
<Tooltip title={item.hint} mouseEnterDelay={500}>
<span>{item.label}</span>
</Tooltip>
) : item.label,
title: enrich(item),
value,
key: value,
isLeaf: item.isLeaf !== false && !item.children,
Expand Down
3 changes: 3 additions & 0 deletions src/components/Node/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ const NodeMinimal: FC<any> = observer(({ node }) => {
});

const useNodeName = (node: any) => {
// @todo sometimes node is control tag, not a region
// @todo and for new taxonomy it can be plain object
if (!node.$treenode) return null;
return getType(node).name as keyof typeof NodeViews;
};

Expand Down
13 changes: 9 additions & 4 deletions src/components/Taxonomy/Taxonomy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import React, {
} from 'react';
import { Dropdown, Menu } from 'antd';

import { LsChevron } from '../../assets/icons';
import { Tooltip } from '../../common/Tooltip/Tooltip';
import { useToggle } from '../../hooks/useToggle';
import { CNTagName } from '../../utils/bem';
import { FF_DEV_4075, FF_PROD_309, isFF } from '../../utils/feature-flags';
import { isArraysEqual } from '../../utils/utilities';
import { LsChevron } from '../../assets/icons';
import TreeStructure from '../TreeStructure/TreeStructure';

import styles from './Taxonomy.module.scss';
import { FF_DEV_4075, FF_PROD_309, isFF } from '../../utils/feature-flags';
import { Tooltip } from '../../common/Tooltip/Tooltip';
import { CNTagName } from '../../utils/bem';

type TaxonomyPath = string[];
type onAddLabelCallback = (path: string[]) => any;
Expand All @@ -33,6 +33,7 @@ type TaxonomyItem = {
};

type TaxonomyOptions = {
canRemoveItems?: boolean,
leafsOnly?: boolean,
showFullPath?: boolean,
pathSeparator?: string,
Expand Down Expand Up @@ -504,6 +505,10 @@ const Taxonomy = ({
const setSelected = (path: TaxonomyPath, value: boolean) => {
const newSelected = value ? [...selected, path] : selected.filter(current => !isArraysEqual(current, path));

// don't remove last item when taxonomy is used as labeling tool
// canRemoveItems is undefined when FF is off; false only when region is active
if (options.canRemoveItems === false && !newSelected.length) return;

setInternalSelected(newSelected);
onChange && onChange(null, newSelected);
};
Expand Down
10 changes: 2 additions & 8 deletions src/mixins/HighlightMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,8 @@ export const HighlightMixin = types
updateSpans() {
if (self._hasSpans || (isFF(FF_LSDV_4620_3) && self._spans?.length)) {
const lastSpan = self._spans[self._spans.length - 1];
const label = self.getLabels();

// label is array, string or null, so check for length
if (!label?.length) {
lastSpan.removeAttribute('data-label');
} else {
lastSpan.setAttribute('data-label', label);
}
Utils.Selection.applySpanStyles(lastSpan, { label: self.getLabels() });
}
},

Expand Down Expand Up @@ -274,7 +268,7 @@ export const HighlightMixin = types
},

getLabels() {
return self.labeling?.mainValue ?? [];
return (self.labeling?.selectedLabels ?? []).map(label => label.value).join(',');
},

getLabelColor() {
Expand Down
13 changes: 13 additions & 0 deletions src/mixins/SelectedChoiceMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ const SelectedChoiceMixin = types

return isDefined(choice1) && isDefined(choice2) && choice1 === choice2;
},
// @todo it's better to only take final values into account
// @todo (meaning alias only, not alias + value when alias is present)
// @todo so this should be the final and simpliest method
hasChoiceSelectionSimple(choiceValue) {
if (choiceValue?.length) {
// grab the string value; for taxonomy, it's the last value in the array
const selectedValues = self.selectedValues().map(s => Array.isArray(s) ? s.at(-1) : s);

return choiceValue.some(value => selectedValues.includes(value));
}

return self.isSelected;
},
hasChoiceSelection(choiceValue, selectedValues = []) {
if (choiceValue?.length) {
// @todo Revisit this and make it more consistent, and refactor this
Expand Down
15 changes: 5 additions & 10 deletions src/regions/Result.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,19 +140,14 @@ const Result = types
return self.mainValue?.join(joinstr) || '';
},

// @todo check all usages of selectedLabels:
// — check usages of non-array values (like `if selectedValues ...`)
// - check empty labels, they should be returned as an array
get selectedLabels() {
if (self.type === 'taxonomy') {
const sep = self.from_name.pathseparator;
const join = self.from_name.showfullpath;

return (self.mainValue || [])
.map(v => join ? v.join(sep) : v.at(-1))
.map(v => ({ value: v, id: v }));
}
if (self.mainValue?.length === 0 && self.from_name.allowempty) {
return self.from_name.findLabel(null);
}
return self.mainValue?.map(value => self.from_name.findLabel(value)).filter(Boolean);
return self.mainValue?.map(value => self.from_name.findLabel(value)).filter(Boolean) ?? [];
},

/**
Expand Down Expand Up @@ -212,7 +207,7 @@ const Result = types

get style() {
if (!self.tag) return null;
const fillcolor = self.tag.background || self.tag.parent.fillcolor;
const fillcolor = self.tag.background || self.tag.parent?.fillcolor;

if (!fillcolor) return null;
const strokecolor = self.tag.background || self.tag.parent.strokecolor;
Expand Down
4 changes: 3 additions & 1 deletion src/tags/control/Choice.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ import { HintTooltip } from '../../components/Taxonomy/Taxonomy';
* @param {string} [alias] - Alias for the choice. If used, the alias replaces the choice value in the annotation results. Alias does not display in the interface.
* @param {style} [style] - CSS style of the checkbox element
* @param {string} [hotkey] - Hotkey for the selection
* @param {string} [html] - can be used to show enriched content[^FF_DEV_2007], it has higher priority than `value`, however `value` will be used in the exported result (should be properly escaped)
* @param {string} [html] - Can be used to show enriched content[^FF_DEV_2007], it has higher priority than `value`, however `value` will be used in the exported result (should be properly escaped)
* @param {string} [hint] - Hint for choice on hover[^FF_PROD_309]
* @param {string} [color] - Color for Taxonomy item
*/
const TagAttrs = types.model({
...(isFF(FF_DEV_3391) ? { id: types.identifier } : {}),
Expand All @@ -54,6 +55,7 @@ const TagAttrs = types.model({
value: types.maybeNull(types.string),
hotkey: types.maybeNull(types.string),
style: types.maybeNull(types.string),
color: types.maybeNull(types.string),
...(isFF(FF_DEV_2007) ? { html: types.maybeNull(types.string) } : {}),
...(isFF(FF_PROD_309) ? { hint: types.maybeNull(types.string) } : {}),
});
Expand Down
39 changes: 39 additions & 0 deletions src/tags/control/Taxonomy/Taxonomy.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ function traverse(root) {
const depth = parents.length;
const obj = { label, path, depth, hint };

if (node.color) obj.color = node.color;
if (node.children) {
obj.children = visitUnique(node.children, path);
}
Expand Down Expand Up @@ -150,6 +151,10 @@ const TaxonomyLabelingResult = types

return self.annotation.results.find(r => r.from_name === self && r.area === area);
},
get canRemoveItems() {
if (!self.isLabeling) return true;
return !self.result;
},
}))
.actions(self => {
const Super = {
Expand All @@ -163,6 +168,35 @@ const TaxonomyLabelingResult = types
self.result.area.setValue(self);
}
},

/**
* @param {string[]} path saved value from Taxonomy
* @returns quazi-label object to act as Label in most places
*/
findLabel(path) {
let title = '';
let items = self.items;
let item;

for (const value of path) {
item = items?.find(item => item.path.at(-1) === value);

if (!item) return null;

items = item.children;
title = self.showfullpath && title ? title + self.pathseparator + item.label : item.label;
}

const label = { value: title, id: path.join(self.pathseparator) };

if (item.color) {
// to conform the current format of our Result#style (and it requires parent)
label.background = item.color;
label.parent = {};
}

return label;
},
};
});

Expand Down Expand Up @@ -411,6 +445,10 @@ const Model = types
},

onChange(_node, checked) {
// don't remove last label from region if region is selected (so canRemoveItems is false)
// should be checked only for Taxonomy as labbeling tool
if (self.canRemoveItems === false && !checked.length) return;

self.selected = checked.map(s => s.path ?? s);
self.maxUsagesReached = self.selected.length >= self.maxusages;
self.updateResult();
Expand Down Expand Up @@ -507,6 +545,7 @@ const HtxTaxonomy = observer(({ item }) => {
minWidth: item.minwidth,
dropdownWidth: item.dropdownwidth,
placeholder: item.placeholder,
canRemoveItems: item.canRemoveItems,
};

return (
Expand Down