// jshint esversion: 6 import React from "react"; import { Button } from "@blueprintjs/core"; import { connect } from "react-redux"; import * as globals from "../../globals"; import Category from "./category"; import { AnnotationsHelpers, ControlsHelpers } from "../../util/stateManager"; import AnnoDialog from "./annoDialog"; import AnnoInputs from "./annoInputs"; import AnnoSelect from "./annoSelect"; @connect(state => ({ writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false, schema: state.world?.schema, config: state.config })) class Categories extends React.Component { constructor(props) { super(props); this.state = { createAnnoModeActive: false, newCategoryText: "", categoryToDuplicate: null }; } handleCreateUserAnno = e => { const { dispatch } = this.props; const { newCategoryText, categoryToDuplicate } = this.state; dispatch({ type: "annotation: create category", data: newCategoryText, categoryToDuplicate }); this.setState({ createAnnoModeActive: false, categoryToDuplicate: null, newCategoryText: "" }); e.preventDefault(); }; handleEnableAnnoMode = () => { this.setState({ createAnnoModeActive: true }); }; handleDisableAnnoMode = () => { this.setState({ createAnnoModeActive: false, categoryToDuplicate: null, newCategoryText: "" }); }; handleModalDuplicateCategorySelection = d => { this.setState({ categoryToDuplicate: d }); }; categoryNameError = name => { /* return false if this is a LEGAL/acceptable category name or NULL/empty string, or return an error type. */ /* allow empty string */ if (name === "") return false; /* test for uniqueness against *all* annotation names, not just the subset we render as categorical. */ const { schema } = this.props; const allCategoryNames = schema.annotations.obs.columns.map(c => c.name); /* check category name syntax */ const error = AnnotationsHelpers.annotationNameIsErroneous(name); if (error) { return error; } /* disallow duplicates */ if (allCategoryNames.indexOf(name) !== -1) { return "duplicate"; } /* otherwise, no error */ return false; }; categoryNameErrorMessage = name => { const err = this.categoryNameError(name); if (err === false) return null; const errorMessageMap = { /* map error code to human readable error message */ "empty-string": "Blank names not allowed", duplicate: "Name must be unique", "trim-spaces": "Leading and trailing spaces not allowed", "illegal-characters": "Only alphanumeric and special characters (-_.) allowed", "multi-space-run": "Multiple consecutive spaces not allowed" }; const errorMessage = errorMessageMap[err] ?? "error"; return {errorMessage}; }; handleNewCategoryText = txt => { this.setState({ newCategoryText: txt }); }; handleChoice = e => { /* Blueprint Suggest format */ this.setState({ newCategoryText: e.target }); }; handleSuggestActiveItemChange = () => {}; render() { const { createAnnoModeActive, categoryToDuplicate, newCategoryText } = this.state; const { categoricalSelection, writableCategoriesEnabled, schema, config } = this.props; /* all names, sorted in display order. Will be rendered in this order */ const allCategoryNames = ControlsHelpers.selectableCategoryNames( schema, ControlsHelpers.maxCategoryItems(config) ).sort(); return (