Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: LEAP-1351: LSF customization for bulk annotation #6203

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
37 changes: 26 additions & 11 deletions web/libs/editor/src/components/App/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ import "../../tags/visual";
import { Space } from "../../common/Space/Space";
import { Button } from "../../common/Button/Button";
import { Block, Elem } from "../../utils/bem";
import { FF_DEV_1170, FF_DEV_3873, FF_LSDV_4620_3_ML, FF_SIMPLE_INIT, isFF } from "../../utils/feature-flags";
import {
FF_BULK_ANNOTATION,
FF_DEV_1170,
FF_DEV_3873,
FF_LSDV_4620_3_ML,
FF_SIMPLE_INIT,
isFF,
} from "../../utils/feature-flags";
import { sanitizeHtml } from "../../utils/html";
import { reactCleaner } from "../../utils/reactCleaner";
import { guidGenerator } from "../../utils/unique";
Expand Down Expand Up @@ -219,6 +226,7 @@ class App extends Component {
</Block>
);

const shouldShowPanels = isDefined(store) && (!isFF(FF_BULK_ANNOTATION) || !store.hasInterface("bulk:annotation"));
const outlinerEnabled = isFF(FF_DEV_1170);
const newUIEnabled = isFF(FF_DEV_3873);

Expand Down Expand Up @@ -260,16 +268,23 @@ class App extends Component {
>
{outlinerEnabled ? (
newUIEnabled ? (
<SideTabsPanels
panelsHidden={viewingAll}
currentEntity={as.selectedHistory ?? as.selected}
regions={as.selected.regionStore}
showComments={store.hasInterface("annotations:comments")}
focusTab={store.commentStore.tooltipMessage ? "comments" : null}
>
{mainContent}
{store.hasInterface("topbar") && <BottomBar store={store} />}
</SideTabsPanels>
shouldShowPanels ? (
yyassi-heartex marked this conversation as resolved.
Show resolved Hide resolved
<SideTabsPanels
panelsHidden={viewingAll}
currentEntity={as.selectedHistory ?? as.selected}
regions={as.selected.regionStore}
showComments={store.hasInterface("annotations:comments")}
focusTab={store.commentStore.tooltipMessage ? "comments" : null}
>
{mainContent}
{store.hasInterface("topbar") && <BottomBar store={store} />}
</SideTabsPanels>
) : (
<>
{mainContent}
{store.hasInterface("topbar") && <BottomBar store={store} />}
</>
)
) : (
<SidePanels
panelsHidden={viewingAll}
Expand Down
10 changes: 7 additions & 3 deletions web/libs/editor/src/components/BottomBar/BottomBar.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { observer } from "mobx-react";
import { Block, Elem } from "../../utils/bem";
import { Actions } from "./Actions";
import { Controls } from "./Controls";
import { Controls, CustomControls } from "./Controls";
import "./BottomBar.styl";
import { FF_DEV_3873, isFF } from "../../utils/feature-flags";
import { FF_BULK_ANNOTATION, FF_DEV_3873, isFF } from "../../utils/feature-flags";

export const BottomBar = observer(({ store }) => {
const annotationStore = store.annotationStore;
Expand All @@ -20,7 +20,11 @@ export const BottomBar = observer(({ store }) => {
<Elem name="group">
{store.hasInterface("controls") && (store.hasInterface("review") || !isPrediction) && (
<Elem name="section" mod={{ flat: true }}>
<Controls annotation={entity} />
{isFF(FF_BULK_ANNOTATION) && store.hasInterface("controls:custom") ? (
Gondragos marked this conversation as resolved.
Show resolved Hide resolved
<CustomControls annotation={entity} />
) : (
<Controls annotation={entity} />
)}
</Elem>
)}
</Elem>
Expand Down
39 changes: 39 additions & 0 deletions web/libs/editor/src/components/BottomBar/Controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,45 @@ const controlsInjector = inject(({ store }) => {
};
});

const CustomControl = observer(({ button }) => {
const look = button.disabled ? "disabled" : button.look ?? "primary";
const [waiting, setWaiting] = useState(false);
const clickHandler = useCallback(
async (e) => {
setWaiting(true);
await button.onClick(e, button);
setWaiting(false);
},
[button, button.onClick],
);
return (
<ButtonTooltip key={button.key} title={button.tooltip}>
<Button
aria-label={button.ariaLabel}
disabled={button.disabled}
look={look}
onClick={clickHandler}
waiting={waiting}
>
{button.title}
</Button>
</ButtonTooltip>
);
});

export const CustomControls = controlsInjector(
observer(({ store }) => {
const buttons = store.controlButtons;
return (
<Block name="controls">
{buttons.map((button) => (
<CustomControl button={button} />
))}
</Block>
);
}),
);

export const Controls = controlsInjector(
observer(({ store, history, annotation }) => {
const isReview = store.hasInterface("review") || annotation.canBeReviewed;
Expand Down
23 changes: 23 additions & 0 deletions web/libs/editor/src/core/CustomTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,30 @@ const CSSColor = types.custom<any, string>({
},
});

// This type is using to store a raw callback function in mobx-state-tree model.
// It won't be serialized right. However, it is useful to allow passing a callback function as a prop
// and be able to get a reaction on its change
//
// /!\ Avoid using this type in case you need serialization of the model it contains
const RawCallback = types.custom<Function, Function>({
Gondragos marked this conversation as resolved.
Show resolved Hide resolved
name: "rawCallback",
fromSnapshot(value: Function) {
return value;
},
toSnapshot(value: Function) {
return value;
},
getValidationMessage(value: Function) {
if (this.isTargetType(value)) return "";
return `Value ${value} is not a function.`;
},
isTargetType(value: any) {
return typeof value === "function";
},
});

export const customTypes = {
range: Range,
color: CSSColor,
rawCallback: RawCallback,
};
6 changes: 5 additions & 1 deletion web/libs/editor/src/core/Tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { IAnyComplexType, IAnyStateTreeNode } from "mobx-state-tree/dist/in

import Registry from "./Registry";
import { parseValue } from "../utils/data";
import { FF_DEV_3391, isFF } from "../utils/feature-flags";
import { FF_BULK_ANNOTATION, FF_DEV_3391, isFF } from "../utils/feature-flags";
import { guidGenerator } from "../utils/unique";

interface ConfigNodeBaseProps {
Expand Down Expand Up @@ -234,6 +234,10 @@ function renderItem(ref: IAnyStateTreeNode, annotation: IAnnotation, includeKey
const typeName = type.name;
const View = Registry.getViewByModel(typeName);

if (isFF(FF_BULK_ANNOTATION) && annotation?.store?.hasInterface("bulk:annotation") && el.isIndependent !== true) {
return null;
}

if (!View) {
throw new Error(`No view for model: ${typeName}`);
}
Expand Down
9 changes: 9 additions & 0 deletions web/libs/editor/src/stores/AppStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import Utils from "../utils";
import { guidGenerator } from "../utils/unique";
import { clamp, delay, isDefined } from "../utils/utilities";
import AnnotationStore from "./Annotation/store";
import { ControlButton } from "./ControlButton";
import Project from "./ProjectStore";
import Settings from "./SettingsStore";
import Task from "./TaskStore";
import { UserExtended } from "./UserStore";
import { UserLabels } from "./UserLabels";
import {
FF_BULK_ANNOTATION,
FF_CUSTOM_SCRIPT,
FF_DEV_1536,
FF_LSDV_4620_3_ML,
Expand Down Expand Up @@ -158,6 +160,8 @@ export default types
queueTotal: types.optional(types.number, 0),

queuePosition: types.optional(types.number, 0),

...(isFF(FF_BULK_ANNOTATION) ? { controlButtons: types.optional(types.array(ControlButton), []) } : {}),
})
.preProcessSnapshot((sn) => {
// This should only be handled if the sn.user value is an object, and converted to a reference id for other
Expand Down Expand Up @@ -932,6 +936,10 @@ export default types
self.setUsers(uniqBy([...newUsers, ...oldUsers], "id"));
}

function setControls(controls) {
self.controlButtons = cast(controls);
}

return {
setFlags,
addInterface,
Expand Down Expand Up @@ -965,6 +973,7 @@ export default types
toggleComments,
toggleSettings,
toggleDescription,
setControls,

setAutoAnnotation,
setAutoAcceptSuggestions,
Expand Down
19 changes: 19 additions & 0 deletions web/libs/editor/src/stores/ControlButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { applySnapshot, getSnapshot, types } from "mobx-state-tree";
import { customTypes } from "../core/CustomTypes";
import { guidGenerator } from "../utils/unique";

export const ControlButton = types
.model("ControlButton", {
id: types.optional(types.identifier, guidGenerator),
title: types.maybe(types.string),
look: types.maybe(types.enumeration(["primary", "danger", "destructive", "alt", "outlined", "active", "disabled"])),
tooltip: types.maybe(types.string),
ariaLabel: types.maybe(types.string),
disabled: types.maybe(types.boolean),
onClick: types.maybe(customTypes.rawCallback),
})
.actions((self) => ({
updateProps(newProps: Partial<typeof self>) {
applySnapshot(self, Object.assign({}, getSnapshot(self), newProps));
},
}));
4 changes: 4 additions & 0 deletions web/libs/editor/src/tags/control/Choice.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ const Model = types
isReadOnly() {
return self.readonly || self.parent?.isReadOnly();
},
// Indicates that it could exist without information about objects, taskData and regions
get isIndependent() {
return true;
},
}))
.volatile(() => ({
// `selected` is a predefined parameter, we cannot use it for state, so use `sel`
Expand Down
5 changes: 5 additions & 0 deletions web/libs/editor/src/tags/control/ClassificationBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ const ClassificationBase = types
}
return self.annotation.results.find((r) => r.from_name === self);
},

// Indicates that it could exist without information about objects, taskData and regions
get isIndependent() {
return self.isClassificationTag && !self.perregion && !self.peritem && !self.value;
},
};
})
.actions((self) => {
Expand Down
Loading
Loading