Skip to content

Vue-component for ontology lookup based on a vsm-dictionary

License

Notifications You must be signed in to change notification settings

vsm/vsm-autocomplete

Repository files navigation

vsm-autocomplete


Intro

vsm-autocomplete is a web-page component for looking up terms that are linked to identifiers. These are fetched from a given vsm-dictionary.

Its main purpose is to be a subcomponent of vsm-box. But it can also be used in other applications that need an input-field with term+ID lookup.


Main functionality

  • vsm-autocomplete is a Vue.js-based input field component, that a web developer can embed inside one of these:
    • A larger Vue component. This is the use-case of vsm-box.
    • A project that bundles it with a particular vsm-dictionary.
      E.g. one could make: vsm-autocomplete + vsm-dictionary-bioportal + a webpack setup,
      to create a standalone web-component: vsm-autocomplete-bioportal.
    • A full Vue application.

  • It makes a new HTML-tag available: '<vsm-autocomplete>', which requires at least a 'vsm-dictionary="..."' attribute.
    This attribute points to an object of type 'VsmDictionary', which is an interface module that connects to a particular database server, in order to get terms and identifiers. The particular interface module could query e.g. BioPortal or PubDictionaries.

  • When a user starts typing, the component queries the vsm-dictionary for matching terms,
    and shows them in a selection panel underneath the input.

  • When the user selects a term (e.g. clicks on a selection-panel item), an item-select signal is emitted, together with the term, ID, and other info.
    This can be picked up with an event listener, as in the example below.

  • Terms, IDs, and styles:
    For being useful in biological domains (as explained on the VSM-pages), vsm-autocomplete supports what vsm-dictionary supports too, i.a.:
    • There can be multiple terms (synonyms) per ID. — These will get shown as separate selection-panel items (different terms, same ID).
    • Any term can have multiple IDs. — These will get shown as separate selection-panel items (same term, different IDs). (Think e.g. of gene names: one term/string often represents multiple genes).
    • There can be "fixed terms". These are 'preferred matches' that result in matching items that appear on top of the selection-panel (also for an empty search string).
    • Terms can include any special character (like 'β-carotene').
    • Terms can have some HTML-styling. E.g. italic style for human gene names, or parts in superscript for charged ions (see string-style-html).

Demo

To see an interactive demo (that uses example dictionary-data), run:

git clone [email protected]:vsm/vsm-autocomplete.git
cd vsm-autocomplete
npm install
npm start

Example use

Here we embed it in another Vue component, e.g. in a MyComp.vue:

<template>
  <div class="my-comp">
    <vsm-autocomplete
      :vsm-dictionary="vsmDictionary"
      :autofocus="true"
      initial-str="a"
      @item-select="onItemSelect"
    />
  </div>
</template>


<script>
// 1.) Create a VsmDictionary data source.
import VsmAutocomplete from 'vsm-autocomplete';
import VsmDictionaryLocal from 'vsm-dictionary-local';  // Or any other one.
import cacher from 'vsm-dictionary-cacher';        // Recommended for speed.

var CachedVsmDictionaryLocal = cacher(VsmDictionaryLocal);
var dict = new CachedVsmDictionaryLocal({      // Initialize with demo data:
  dictData: [
    { id: 'Dict1',
      name: 'Example subdictionary 1',
      entries: [
        { id: 'D1:001', terms: ['aaa', 'aasynonym'], descr: 'description' },
        { id: 'D1:002', terms: 'aab', descr: 'descr2' },
        { id: 'D1:003', terms: 'abc', descr: 'descr3' },
        { id: 'D1:004', terms: [{ str: 'acd', style:'i' }], descr: 'descr4' }
      ]
    }
  ]
});

// 2.) Create a parent component 'MyComp' that embeds a VsmAutocomplete.
export default {
  name: 'MyComp',

  components: {
    'vsm-autocomplete': VsmAutocomplete  // Use it as a sub-component of MyComp.
  },

  data: function() {
    return {
      vsmDictionary: dict
    };
  },

  methods: {
    onItemSelect(match) {
      console.log(match.id, match.str, match.descr, JSON.stringify(match.terms));
    }
  }
};
</script>


<style scoped>  /* Add CSS-styling around the vsm-autocomplete */
.my-comp {
  width: 200px;
  border: 1px solid #ddd;
}
</style>


The example shows that the <vsm-autocomplete> HTML-tag accepts two types of attributes:

  • props:
    These insert information, settings, or functions, into the component.
    Here: :vsm-dictionary="...", :autofocus="...", and initial-str="...".
  • event-listeners:
    These listen to 'event' signals and information, that come out of the component.
    Here: @item-select="...".

About the 'item-literal'

An extra 'item-literal' can be shown at the bottom of the autocomplete selection-list.

  • This list-item is only visible if a @item-literal-select event-listener is attached.
  • When the user selects the 'item-literal', a parent component (e.g. vsm-box) can interpret this as a signal that the user wants to access some more advanced term-search dialog window.
  • It does not represent a match-object from the VsmDictionary, so it has no ID.
    • It represents the input-field's literal string.
    • It has its own CSS-style to make it visually distinct.
    • One can generate custom content for it, via the :custom-item-literal prop.

Available props

Prop Type Required Default Description
vsm-dictionary Object true A subclass of VsmDictionary: an interface to a
provider of terms, IDs, styling info, subdictionary info, refTerms and fixedTerms
autofocus Boolean false When true, automatically focuses the input-field at page load
placeholder String|Boolean false • String: shows this placeholder message in an empty input-field |
• false/absent: adds none
initial-value String '' The initial content for the input-field
query-options Object { perPage: 20 } Options for filters, fixedTerms, etc., sent along with calls to vsmDictionary.getMatchesForString()
max-string-lengths Object { str: 40, strAndDescr: 70 } Limits the length of matches' str and descr shown in list-items
(in number of characters)
fresh-list-delay Number 0 Prevents selection of matches in the list, until this amount of milliseconds has passed
custom-item Function|Boolean false • Function: returns HTML-content for each part of normal list-items (see below) |
• false/absent: default content is used
custom-item-literal Function|Boolean false • Function: returns HTML-content (see below) for the item-literal |
• false/absent: the default item-literal is used


Notes:

  • The placeholder is hidden when the input-field is focused.
  • The query-options's perPage sets the length of the selection-list.
    • This includes S/T-type items only, while N/R/F/G-type items (see VsmDictionary's spec) and a item-literal may get added in addition.
  • The query-options's idts specifies a list of fixedTerms (see vsm-dictionary's spec), only if this autocomplete component should use any.
  • In the selection-panel, N/R/F/G-type items get CSS-styling that makes them visually distinct.
  • The max-string-lengths's strAndDescr sets the maximum length of an already length-trimmed str, plus its descr.
  • When a selection-list shows stale results (meaning that the list is expected to be replaced with fresh results soon), then it will not respond to Enter or Click until the new results appear.
    But if a user decides to Enter/Click on stale results anyway, and the list would update with new results shortly before that Enter actually comes through, then the Enter would select a different list-item than intended: an item that freshly appeared there in a new list; and perhaps the user would not even notice that a different item was selected.
    Therefore, by setting a fresh-list-delay (of e.g. a few 100 milliseconds), one can delay when the list starts to respond, and prevent an impatient user from mistakenly Entering/selecting an item that only just appeared.
  • The custom-item and custom-item-literal functions are described in the "Customized content" section, further below.

Available events

Event Output Description (-> output)
focus When the input-field gets the page focus
blur When the input-field loses the focus
input-change String When the input-field's content changes, after trimming (-> the latest value, trimmed)
input String When the input-field's content changes (-> the latest value)
key-esc When Esc is pressed, while the selection-list is closed
key-bksp When Backspace is pressed, while the input-field is empty
key-ctrl-enter When Ctrl+Enter is pressed, while the input-field has no string-codes
key-shift-enter When Shift+Enter is pressed
key-tab String When Tab or Shift+Tab is pressed (-> modifier key: '' or 'shift')
dblclick When the input-field is double-clicked
mouseover-input When the input-field is mouse-hovered (not the selection-list)
list-open When the selection-list opens
list-close When the selection-list closes
item-active-change false|Object|String When the active item in the selection-list changes
-> • false: none (on list-close) |
• Object: a match-object from VsmDictionary |
• String: the literal input string (for item-literal)
item-select Object When an item is selected (-> a match-object from VsmDictionary)
item-literal-select String When the item-literal is selected (-> the literal input string)


Notes:

  • When a @key-tab listener is attached, vsm-autocomplete prevents the default behaviour:
    i.e. it does not let the focus move away from the input-field.
    Because in that case it means that a parent component wants to decide what happens on Tab / Shift-Tab.
  • Please note the conditions stated above for @key-esc, @key-bksp, and @key-ctrl-enter.
  • The user can press Ctrl+Enter to convert certain string-codes in the input (if any) to special characters, e.g. '\beta' to β.
    Only if no codes were replaced, it emits key-ctrl-enter, which an external listener may act upon.
  • @item-select is likely the most important event.

Various info


Subcomponents: tree overview

This is the architecture of vsm-autocomplete in terms of its own subcomponents:

VsmAutocomplete
├── TheInput
├─┬ TheList
│ ├── ListItem       (multiple items)
│ └── ListItemLiteral  (only 1, or 0)
└── TheSpinner

FixedTerms pre-loading

If this autocomplete component should take into account some fixedTerms, i.e. if it is given some queryOptions.idts[] (see vsm-dictionary's spec) as prop,
then it makes sure that this fixedTerm data is pre-loaded in the given vsmDictionary, before making any requests for matching strings to it.


Interactivity specification

See the tests for details.
They are easiest to read in the following, bottom-up order:

  • subcomponents/TheInput.test.js
  • subcomponents/ListItem.test.js
  • subcomponents/ListItemLiteral.test.js
  • subcomponents/TheList.test.js
  • (there are no tests for the subcomponents/TheSpinner, as it contains no logic)
  • VsmAutocomplete.test.js

Customized content


Custom content for ListItems

The selection-panel items are given a default content like:
    match-string (description) (dictionary-ID),
and each of the three parts may show extra info when the user mouse-hovers it; e.g. if "(descri...)" was cut short, then hovering shows the full description.

Any of these parts can be customized by giving a customizing function as the custom-item prop.
 • E.g.: for items that represent a match from a Human Genes subdictionary, it could insert a list of gene-name synonyms in the description part.
 • Or: for a list of items representing a mix of matches from a Human Genes and a Mouse Genes subdictionary resp., it could add an image of the species so that users can more quickly distinguish between identically named matches.

customItem() (if given) is called during the construction of each ListItem.
It is called with one argument: an Object with useful properties, representing useful information for building a custom ListItem: customItem(data):

  • data: {Object}:
    • item: the complete 'match'-object that this ListItem represents. (The 'match' data-type is described in VsmDictionary's spec).
    • searchStr: the string that the user typed to find this match.
    • maxStringLengths: the max-string-lengths prop that was set on this <vsm-autocomplete>.
    • queryOptions: the prop query-options that was set on this <vsm-autocomplete>.
      (Use case: by evaluating queryOptions.filter.dictIDs[], a multi-purpose customization function could detect if this particular autocomplete is combining gene-IDs from multiple species, and if so, add an icon (image of the species) in front of gene-names for clarity).
    • dictInfo: the info-object of the subdictionary from which this match came (which contains customItem()).
    • vsmDictionary: the VsmDictionary instance being used.
      (It can not be used for queries, as customItem() is synchronous. But it may be used for accessing e.g. vsmDictionary.numberMatchConfig details).
    • strs: the default content for the different parts of the ListItem.
      It is an Object with properties (Strings) that represent the different parts.
      The parts may contain HTML tags, but will most commonly be just text.
      All properties are guaranteed to be of type {String}.
      This default content will be used if customItem() does not change it.
      • str: the match's term-string, which is matchObj.str,
        but trimmed in length according to maxStringLengths.str,
        and with matchObj.style's custom CSS-styling already applied.
      • descr: the match's description, which is matchObj.descr,
        but trimmed in length according to maxStringLengths.strAndDescr.
      • info: the dictionary-ID, which is matchObj.dictID;
        (or for number-string matches: the number-ID; or for refTerms: empty).
      • strTitle: the str-part's HTML 'title=".."' attribute,
        which appears on mouse-hover.
        (If str was length-trimmed, then this is matchObj.str, else empty).
      • descrTitle: the descr-part's HTML 'title=".."' attribute.
        (If descr was length-trimmed, then this is matchObj.descr, else empty).
      • infoTitle: the info-part's HTML 'title=".."' attribute.
        (This is a text with matchObj.id and dictInfo.name).
      • extra: an extra part that is added at the end of the ListItem, and which is empty by default.

customItem() must return an Object like its argument data.strs.
It may simply return the given strs object, after directly modifying just those properties it needs to.

  • E.g. customItem: data => { if (data.item.dictID == 'X') data.strs.descr += '!'; return data.strs; }
    would add a "!" to the description-part of each ListItem from subdictionary 'X' (only), and leave these ListItems' other parts unchanged.
  • If any part is empty, it will be left out of the ListItem.

Custom content for the ListItemLiteral

The item-literal can be customized by giving a customizing function as the custom-item-literal prop.

customItemLiteral() (if given) is called during the construction of a ListItemLiteral.
It is called with one argument: an Object with useful properties:
customItemLiteral(data):

  • data: {Object}:
    • searchStr: the string that the user typed in TheInput.
    • maxStringLengths: the max-string-lengths prop that was set on this <vsm-autocomplete>.
    • strs: the default content for the different parts of the ListItemLiteral (similar to customItem's data.strs):
      • str: the default (text/HTML) content for the ListItemLiteral, which is:
        the content of the input-field, trimmed in length according to max-string-lengths.
      • strTitle: its HTML-element's 'title=".."' attribute, which appears on mouse-hover.

customItemLiteral() must return an Object like its argument data.strs.
It may simply return the given strs object, after directly modifying just those properties it needs to.

  • E.g. customItemLiteral: data => { data.strs.strTitle = ''; return data.strs }
    would only remove the item-literal's 'title' attribute.

Custom CSS

One can change the look of a <vsm-autocomplete> component by overriding its CSS-classes.
As an example, the following 'CSS skin' makes everything a bit larger: vsm-autocomplete-large.css.


License

This project is licensed under the AGPL license - see LICENSE.md.

About

Vue-component for ontology lookup based on a vsm-dictionary

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •