diff --git a/package.json b/package.json
index dd037d3a..0964adf9 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
"start": "node dev-server.js",
"test": "echo \"Error: no test specified\" && exit 0",
"lint": "yarn run eslint ./src"
- },
+ },
"author": "Heartex, Inc.",
"license": "ISC",
"devDependencies": {
@@ -80,7 +80,7 @@
"react-dom": "^17.0.2",
"react-hot-loader": "^4.12.20",
"react-hotkeys-hook": "^2.4.0",
- "react-icons": "^3.11.0",
+ "react-icons": "^4.11.0",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.6",
"react-window-infinite-loader": "^1.0.5",
diff --git a/src/components/DataManager/DataManager.js b/src/components/DataManager/DataManager.js
index 71899ab3..604bbf68 100644
--- a/src/components/DataManager/DataManager.js
+++ b/src/components/DataManager/DataManager.js
@@ -12,6 +12,8 @@ import { DataView } from "../MainView";
import "./DataManager.styl";
import { Toolbar } from "./Toolbar/Toolbar";
+const SIMILARITY_UPPER_LIMIT_PRECISION = 10;
+
const injector = inject(({ store }) => {
const { sidebarEnabled, sidebarVisible } = store.viewsStore ?? {};
@@ -33,11 +35,17 @@ const summaryInjector = inject(({ store }) => {
SDK,
};
} else {
+ const similarityUpperLimit = taskStore?.similarityUpperLimit ?
+ (Math.ceil(taskStore?.similarityUpperLimit * SIMILARITY_UPPER_LIMIT_PRECISION) / SIMILARITY_UPPER_LIMIT_PRECISION)
+ :
+ null;
+
return {
totalTasks: project?.task_count ?? project?.task_number ?? 0,
totalFoundTasks: taskStore?.total ?? 0,
totalAnnotations: taskStore?.totalAnnotations ?? 0,
totalPredictions: taskStore?.totalPredictions ?? 0,
+ similarityUpperLimit,
cloudSync: project.target_syncing ?? project.source_syncing ?? false,
};
}
diff --git a/src/components/DataManager/DataManager.styl b/src/components/DataManager/DataManager.styl
index f43f69b2..d0b79ae2 100644
--- a/src/components/DataManager/DataManager.styl
+++ b/src/components/DataManager/DataManager.styl
@@ -9,4 +9,4 @@
width 100%
&_shrink
- width calc(100% - 350px)
+ width calc(100% - 420px)
diff --git a/src/components/Filters/FilterLine/FilterLine.js b/src/components/Filters/FilterLine/FilterLine.js
index 7a3e49ff..85e97a50 100644
--- a/src/components/Filters/FilterLine/FilterLine.js
+++ b/src/components/Filters/FilterLine/FilterLine.js
@@ -1,6 +1,6 @@
-import { observer } from "mobx-react";
+import { inject, observer } from "mobx-react";
import React, { Fragment } from "react";
-import { FaTrash } from "react-icons/fa";
+import { LiaTimesSolid } from "react-icons/lia";
import { BemWithSpecifiContext } from "../../../utils/bem";
import { Button } from "../../Common/Button/Button";
import { Icon } from "../../Common/Icon/Icon";
@@ -8,6 +8,7 @@ import { Tag } from "../../Common/Tag/Tag";
import { FilterDropdown } from "../FilterDropdown";
import "./FilterLine.styl";
import { FilterOperation } from "./FilterOperation";
+import { getRoot } from "mobx-state-tree";
const { Block, Elem } = BemWithSpecifiContext();
@@ -29,15 +30,31 @@ const Conjunction = observer(({ index, view }) => {
const GroupWrapper = ({ children, wrap = false }) => {
return wrap ? {children} : children;
};
+const injector = inject(({ store }) => ({
+ store,
+}));
+const CustomFilter = ({ CustomFilterLine, ...rest }) => ;
-export const FilterLine = observer(({
+export const FilterLine = injector(observer(({
filter,
availableFilters,
index,
view,
sidebar,
dropdownClassName,
+ store,
}) => {
+ const customColumn = getRoot(view).SDK?.customColumns?.[filter.field.alias];
+ const CustomFilterLine = customColumn?.renderFilter?.(
+ filter,
+ availableFilters,
+ index,
+ view,
+ sidebar,
+ dropdownClassName,
+ store,
+ );
+
return (
@@ -56,7 +73,12 @@ export const FilterLine = observer(({
width={80}
dropdownWidth={120}
dropdownClassName={dropdownClassName}
- onChange={(value) => filter.setFilterDelayed(value)}
+ onChange={(value) => {
+ const selectedAlias = value.split(":").pop();
+ const customFilter = getRoot(view).SDK?.customColumns?.[selectedAlias];
+
+ customFilter ? customFilter.onFilterAdd?.(value, filter, view) : filter.setFilterDelayed(value);
+ }}
optionRender={({ item: { original: filter } }) => (
{filter.field.title}
@@ -76,24 +98,38 @@ export const FilterLine = observer(({
-
+ {CustomFilterLine ? (
+
+
+
+ ) : (
+
+ )}
);
},
-);
+));
diff --git a/src/components/Filters/FilterLine/FilterLine.styl b/src/components/Filters/FilterLine/FilterLine.styl
index e347507e..a811b0ba 100644
--- a/src/components/Filters/FilterLine/FilterLine.styl
+++ b/src/components/Filters/FilterLine/FilterLine.styl
@@ -33,6 +33,9 @@
display grid
grid-auto-flow column
+ &_customFilterLine
+ grid-column 3/5;
+
&__group
flex 0
display grid
diff --git a/src/components/Filters/Filters.js b/src/components/Filters/Filters.js
index a98a72d6..6308b222 100644
--- a/src/components/Filters/Filters.js
+++ b/src/components/Filters/Filters.js
@@ -1,6 +1,6 @@
import { inject } from "mobx-react";
import React from "react";
-import { FaCaretSquareRight, FaPlus } from "react-icons/fa";
+import { BsPin } from "react-icons/bs";
import { Block, cn, Elem } from "../../utils/bem";
import { Button } from "../Common/Button/Button";
import { Icon } from "../Common/Icon/Icon";
@@ -51,32 +51,13 @@ export const Filters = injector(({ views, currentView, filters }) => {
return (
-
- {filters.length ? (
- filters.map((filter, i) => (
-
- ))
- ) : (
- No filters applied
- )}
-
{!sidebarEnabled ? (
@@ -86,12 +67,30 @@ export const Filters = injector(({ views, currentView, filters }) => {
size="small"
about="Pin to sidebar"
onClick={() => views.expandFilters()}
- style={{ display: "inline-flex", alignItems: "center" }}
- icon={}
+ style={{ display: "inline-flex", alignItems: "center", border: "none" }}
+ icon={}
/>
) : null}
+
+ {filters.length ? (
+ filters.map((filter, i) => (
+
+ ))
+ ) : (
+ No filters applied
+ )}
+
);
});
diff --git a/src/components/Filters/Filters.styl b/src/components/Filters/Filters.styl
index 7255e8be..2a70357f 100644
--- a/src/components/Filters/Filters.styl
+++ b/src/components/Filters/Filters.styl
@@ -1,10 +1,11 @@
.filters
background-color white
position relative
+ padding-top 15px
&:not(&_sidebar)
padding-top 10px
- min-width: 400px;
+ min-width 625px
border-radius: 2px;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
@@ -37,8 +38,7 @@
&__actions
display flex
- margin-top 10px
- padding 0 10px 10px
+ padding 0 16px 16px
justify-content space-between
&__empty
@@ -47,12 +47,13 @@
color #585858
&__list
- padding 0 10px
+ padding 0 10px 15px
&_withFilters
display grid
grid-template-columns 65px min-content min-content 1fr 24px
grid-gap 3px 4px
+ align-items center
&_sidebar &__list
&_withFilters
@@ -61,6 +62,28 @@
grid-auto-flow row
align-items center
+ .button
+ --button-color rgba(9, 109, 217, 1)
+ box-shadow none
+ border 1px solid rgba(9, 109, 217, 0.16)
+ height 32px
+ font-size 16px
+ font-style normal
+ font-weight 500
+ line-height 24px
+ letter-spacing 0.15px
+ padding 8px 16px
+ &_withIcon
+ padding 0
+ .icon
+ svg
+ width 18px
+ height 18px
+
+ &__list
+ .button
+ border 0 none
+ --button-color rgba(137, 128, 152, 1)
.filter-data-tag
margin 1px
font-size 12px
diff --git a/src/sdk/dm-sdk.js b/src/sdk/dm-sdk.js
index 1270213b..0323d74c 100644
--- a/src/sdk/dm-sdk.js
+++ b/src/sdk/dm-sdk.js
@@ -135,6 +135,7 @@ export class DataManager {
edit: true,
duplicate: true,
}
+ customColumns = {};
/** @type {"dm" | "labelops"} */
type = "dm";
@@ -199,7 +200,7 @@ export class DataManager {
this.actions.set(action.id, { action, callback });
});
}
-
+ this.customColumns = config.customColumns ?? {};
this.type = config.type ?? "dm";
this.initApp();
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index 48e2b9c4..93248799 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -60,6 +60,8 @@ export const AppStore = types
interfaces: types.map(types.boolean),
toolbar: types.string,
+
+ customColumns: types.optional(CustomJSON, {}),
})
.views((self) => ({
/** @returns {import("../sdk/dm-sdk").DataManager} */
diff --git a/src/stores/DataStores/tasks.js b/src/stores/DataStores/tasks.js
index 1ea7c0e1..11b4e532 100644
--- a/src/stores/DataStores/tasks.js
+++ b/src/stores/DataStores/tasks.js
@@ -218,17 +218,19 @@ export const create = (columns) => {
},
postProcessData(data) {
- const { total_annotations, total_predictions } = data;
+ const { total_annotations, total_predictions, similarity_score_upper_limit } = data;
if (total_annotations !== null)
self.totalAnnotations = total_annotations;
if (total_predictions !== null)
self.totalPredictions = total_predictions;
+ if (total_predictions !== null)
+ self.similarityUpperLimit = similarity_score_upper_limit;
},
}))
.preProcessSnapshot((snapshot) => {
- const { total_annotations, total_predictions, ...sn } = snapshot;
+ const { total_annotations, total_predictions, similarity_score_upper_limit, ...sn } = snapshot;
return {
...sn,
@@ -239,6 +241,7 @@ export const create = (columns) => {
})),
totalAnnotations: total_annotations,
totalPredictions: total_predictions,
+ similarityUpperLimit: similarity_score_upper_limit,
};
});
};
diff --git a/src/stores/Tabs/store.js b/src/stores/Tabs/store.js
index a9eb4890..d7472387 100644
--- a/src/stores/Tabs/store.js
+++ b/src/stores/Tabs/store.js
@@ -372,6 +372,8 @@ export const TabStore = types
fetchColumns() {
const columns = self.columnsRaw;
const targets = unique(columns.map((c) => c.target));
+ const rootSDK = getRoot(self).SDK;
+ const customColumns = rootSDK.customColumns ?? {};
const hiddenColumns = {};
const addedColumns = new Set();
@@ -434,7 +436,9 @@ export const TabStore = types
addedColumns.add(column.id);
- if (!col.children && column.filterable && (col?.visibility_defaults?.filter ?? true)) {
+ if (!col.children && column.filterable && (
+ (col?.visibility_defaults?.filter ?? true) || (customColumns[col.id]?.filterable ?? false)
+ )) {
self.availableFilters.push({
id: `filter:${columnID}`,
type: col.type,
diff --git a/src/stores/Tabs/tab.js b/src/stores/Tabs/tab.js
index 72b81e54..9b8bd475 100644
--- a/src/stores/Tabs/tab.js
+++ b/src/stores/Tabs/tab.js
@@ -17,8 +17,6 @@ import { History } from '../../utils/history';
import { FF_DEV_1470, FF_LOPS_12, isFF } from "../../utils/feature-flags";
import { CustomJSON, StringOrNumberID, ThresholdType } from "../types";
-const DEFAULT_THRESHOLD = { min: 0, max: 10 };
-
export const Tab = types
.model("View", {
id: StringOrNumberID,
@@ -53,7 +51,7 @@ export const Tab = types
editable: true,
deletable: true,
semantic_search: types.optional(types.array(CustomJSON), []),
- threshold: types.optional(ThresholdType, DEFAULT_THRESHOLD),
+ threshold: types.optional(types.maybeNull(ThresholdType), null),
})
.volatile(() => {
const defaultWidth = getComputedStyle(document.body).getPropertyValue("--menu-sidebar-width").replace("px", "").trim();
@@ -218,7 +216,7 @@ export const Tab = types
columnsDisplayType: self.columnsDisplayType.toPOJO(),
gridWidth: self.gridWidth,
semantic_search: self.semantic_search?.toJSON() ?? [],
- threshold: self.threshold?.toJSON() ?? DEFAULT_THRESHOLD,
+ threshold: self.threshold?.toJSON(),
};
if (self.saved || apiVersion === 1) {
@@ -305,9 +303,12 @@ export const Tab = types
self.selected = ids;
},
- setSemanticSearch(semanticSearchList) {
+ setSemanticSearch(semanticSearchList, save = true) {
self.semantic_search = semanticSearchList ?? [];
- return self.save();
+ if (self.semantic_search.length === 0) {
+ self.threshold = null;
+ }
+ return save && self.save();
},
setSemanticSearchThreshold(min, max) {
@@ -315,6 +316,11 @@ export const Tab = types
return self.save();
},
+ clearSemanticSearchThreshold(save = true) {
+ self.threshold = null;
+ return save && self.save();
+ },
+
selectAll() {
self.selected.toggleSelectedAll();
},
diff --git a/src/stores/Tabs/tab_filter.js b/src/stores/Tabs/tab_filter.js
index 46315b73..d0dfcf32 100644
--- a/src/stores/Tabs/tab_filter.js
+++ b/src/stores/Tabs/tab_filter.js
@@ -54,9 +54,10 @@ export const TabFilter = types
},
get component() {
- const operationsList = Filters[self.filter.currentType] ?? Filters.String;
+ const rootSDK = getRoot(self)?.SDK;
+ const operationsList = rootSDK?.customColumns?.[self.filter.field.alias]?.operationsList ?? Filters[self.filter.currentType] ?? Filters.String;
- return allowedFilterOperations(operationsList, getRoot(self)?.SDK?.type);
+ return allowedFilterOperations(operationsList, rootSDK?.type);
},
get componentValueType() {
@@ -74,8 +75,10 @@ export const TabFilter = types
get isValidFilter() {
const { currentValue: value } = self;
-
- if (!isDefined(value) || isBlank(value)) {
+ const rootSDK = getRoot(self)?.SDK;
+ const customFilter = rootSDK?.customColumns?.[self.filter.field.alias];
+
+ if ((customFilter && !customFilter.isFilterValid(self)) || !isDefined(value) || isBlank(value)) {
return false;
} else if (FilterValueRange.is(value)) {
return isDefined(value.min) && isDefined(value.max);
@@ -99,7 +102,7 @@ export const TabFilter = types
get cellView() {
const col = self.filter.field;
- return CellViews[col.type] ?? CellViews[toStudlyCaps(col.alias)];
+ return getRoot(self).SDK?.customColumns[col.alias] ?? CellViews[col.type] ?? CellViews[toStudlyCaps(col.alias)];
},
}))
.volatile(() => ({
@@ -137,7 +140,7 @@ export const TabFilter = types
self.setOperator(self.component[0].key);
}
- if (save) self.saved();
+ if (save) self.save();
},
setFilterDelayed(value) {
diff --git a/yarn.lock b/yarn.lock
index cf2b3593..0073b315 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3282,11 +3282,6 @@ camel-case@^4.1.2:
pascal-case "^3.1.2"
tslib "^2.0.3"
-camelcase@^5.0.0:
- version "5.3.1"
- resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz"
- integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-
camelcase@^6.2.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
@@ -6731,12 +6726,10 @@ react-hotkeys-hook@^2.4.0:
dependencies:
hotkeys-js "3.8.1"
-react-icons@^3.11.0:
- version "3.11.0"
- resolved "https://registry.npmjs.org/react-icons/-/react-icons-3.11.0.tgz"
- integrity sha512-JRgiI/vdF6uyBgyZhVyYJUZAop95Sy4XDe/jmT3R/bKliFWpO/uZBwvSjWEdxwzec7SYbEPNPck0Kff2tUGM2Q==
- dependencies:
- camelcase "^5.0.0"
+react-icons@^4.11.0:
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.11.0.tgz#4b0e31c9bfc919608095cc429c4f1846f4d66c65"
+ integrity sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"