diff --git a/my_flask_app/__pycache__/main.cpython-39.pyc b/my_flask_app/__pycache__/main.cpython-39.pyc
index dd62806cbd3120a152b6117f29698eb8b6893a44..3325cd478eea87c234e99721ffb5d80d466b4250 100644
Binary files a/my_flask_app/__pycache__/main.cpython-39.pyc and b/my_flask_app/__pycache__/main.cpython-39.pyc differ
diff --git a/my_flask_app/main.py b/my_flask_app/main.py
index 5495326f0e021c74fe14ed40380bc657c9fc9cb5..9538b70d9fc2a14db135d46fe3dabc68f378c70f 100644
--- a/my_flask_app/main.py
+++ b/my_flask_app/main.py
@@ -1,7 +1,7 @@
 from my_flask_app import app
 from .models.models import CustomTable, CustomColumn, Theme, CompressedDataType, RegType, RegRole
 from flask_sqlalchemy import SQLAlchemy
-from flask import jsonify, redirect, render_template, request, session, url_for
+from flask import jsonify, redirect, render_template, request, session, url_for, json
 from sqlalchemy import ARRAY, BIGINT, BOOLEAN, DOUBLE_PRECISION, FLOAT, INTEGER, JSON, NUMERIC, SMALLINT, TIMESTAMP, UUID, VARCHAR, MetaData, String, create_engine, text, inspect
 import pydot, base64, os, logging
 from sqlalchemy.orm import scoped_session, sessionmaker
@@ -18,7 +18,7 @@ db = SQLAlchemy()
 
 
 app.secret_key = 'my_secret_key'  # Needed for session management
-dropped_items = []
+# dropped_items = []
 
 # Register the custom type with SQLAlchemy's PostgreSQL dialect
 ischema_names['_timescaledb_internal.compressed_data'] = CompressedDataType
@@ -43,6 +43,11 @@ def index():
                 print("3")
                 if database_uri != session.get('db_uri', ''):
                     session['db_uri'] = database_uri
+                    session['target_table_names'] = []
+                    session['target_table_name'] = ''
+                    session['self_defined_labels'] = []
+                    session['schema_selected'] = ''
+                    session['show_all'] = False
                     print("4")
                 # Initialize inspector here, outside the inner if-else
                 print("4.5")
@@ -60,27 +65,38 @@ def index():
                 session_factory = sessionmaker(bind=engine)
                 db.session = scoped_session(session_factory)
                 print("6")
-                print("6.5")
 
             database = database_name_from_uri(engine, database_uri) if database_uri != '' else ''
             schemas = getSchema(insp) if session['db_uri'] != '' else []
             themes = getThemes()
             tables_selected = []
+            dropped_items = session.get('target_table_names', [])
+            self_labels = session.get('self_defined_labels', [])
             schema_Selected = request.form.get('schema', None)
+            if schema_Selected != None:
+                session['schema_selected'] = schema_Selected
+                print("888" + schema_Selected)
             show_all = request.form.get('show_all') == 'True'
+            if show_all != False:
+                session['show_all'] = show_all
+            
 
-            tables1 = importMetadata(engine, schema_Selected, tables_selected, show_all)
+            tables1 = importMetadata(engine, schema_Selected, None, show_all)
             graph_DOT1 = createGraph(tables1, themes["Blue Navy"], True, True, True)
             image1 = generate_erd(graph_DOT1)
 
+            print("111")
+            for name in dropped_items:
+                print(name)
             if dropped_items==[]:
                 image2 = ""
             else:
                 tables2 = importMetadata(engine, None, dropped_items, False)
                 graph_DOT2 = createGraph(tables2, themes["Blue Navy"], True, True, True)
                 image2 = generate_erd(graph_DOT2)
+                print("222")
 
-            return render_template('app.html', database=database, schemas=schemas, show_all=show_all, schema_Selected=schema_Selected, tables=tables1, image1=image1, image2=image2, dropped_items=dropped_items)
+            return render_template('app.html', database=database, schemas=schemas, show_all=show_all, schema_Selected=schema_Selected, tables=tables1, image1=image1, image2=image2, dropped_items=dropped_items, self_labels=self_labels)
         
         else:
             # Display the form
@@ -93,18 +109,38 @@ def index():
 @app.route('/handle-drop', methods=['POST'])
 def handle_drop():
     data = request.json
-    item_name = data.get('item')
+    # if data.get('reset') == True or data.get('reset') != None:
+    #     print(data.get('reset'))
+    #     session['target_table_names'] = []
+    #     dropped_items = []
+    #     graph_DOT2 = "digraph {};"
+    #     image2 = generate_erd(graph_DOT2)
+
+    #     database_uri = session.get('db_uri', '')
+    #     engine = create_engine(database_uri)
+    #     insp = inspect(engine)
+    #     schema_Selected = session.get('schema', None)
+    #     print("999"+ str(schema_Selected))
+    #     show_all = session.get('show_all', False)
+    #     tables_in_CustomTable = importMetadata(engine, schema_Selected, None, show_all)
+    #     tables1 = list(tables_in_CustomTable.keys()) if tables_in_CustomTable != {} else []
+
+    #     return jsonify(image2=image2, dropped_items=dropped_items, tables=tables1)
+
+    item_name = data.get('item') 
     action = data.get('action')
-
+    dropped_items = session.get('target_table_names', '')
+    print("444")
     if action == 'added':
         dropped_items.append(item_name)
     elif action == 'removed' and item_name in dropped_items:
         dropped_items.remove(item_name)
+    session['target_table_names'] = dropped_items
 
     # Regenerate ERD based on the updated dropped_items
-    database = db.engine.url.database
+    engine = create_engine(session['db_uri'])
     themes = getThemes()
-    tables2 = importMetadata(database, None, dropped_items, False)
+    tables2 = importMetadata(engine, None, dropped_items, False)
     graph_DOT2 = createGraph(tables2, themes["Blue Navy"], True, True, True)
     image2 = generate_erd(graph_DOT2)
 
@@ -115,15 +151,58 @@ def handle_drop():
 def get_table_data():
     data = request.json
     table_name = data.get('table_name')
-    print(table_name)
-
-    # Query your database to get the data for the table_name
-    content = query_database_for_table_content(table_name)
+    if table_name == None:
+        return jsonify({ 'html_table': generate_html_table([]), 'table_columns': '' })
+    
+    session['target_table_name'] = table_name
+    engine = create_engine(session['db_uri'])
 
+    content = query_database_for_table_content(engine, table_name)
     # Convert content to HTML table format
     html_table = generate_html_table(content)
 
-    return jsonify({'html_table': html_table})
+    # Get the table columns and values
+    table_instance = getTableInstance(engine, table_name)
+    table_columns = [column.name for column in table_instance.columns]
+    sorted_table_columns = sorted(table_columns)
+
+    return jsonify({ 'html_table': html_table, 'table_columns': sorted_table_columns })
+
+
+@app.route('/get-label-column', methods=['POST'])
+def get_label_column():
+    data = request.json
+    label_column_name = data.get('label_column_name')
+
+    engine = create_engine(session['db_uri'])
+    table_name = session.get('target_table_name', '')
+    session['target_label_column'] = label_column_name if label_column_name != '' else ''
+  
+    labels = showDistinctValues(engine, table_name, label_column_name) if label_column_name != '' else []
+    print("8")
+    print(labels)
+    # Add the self-defined labels from the session
+    target_labels = session.get('self_defined_labels', [])
+    print("9")
+    for label in target_labels:
+        print(label)    
+
+    return jsonify({ 'table_labels': labels, 'defined_labels': target_labels })
+
+
+@app.route('/add-self-defined-label', methods=['POST'])
+def add_label():
+    data = request.json
+    self_defined_label = data.get('self_defined_label')
+    self_defined_labels = session.get('self_defined_labels', [])
+    if self_defined_label not in self_defined_labels:
+        self_defined_labels.append(self_defined_label)
+        session['self_defined_labels'] = self_defined_labels
+    print("777")
+    for label in self_defined_labels:
+        print(label)
+    
+    return jsonify({'defined_labels': self_defined_labels})
 
 
 def database_name_from_uri(engine, database_uri: str):
@@ -156,29 +235,30 @@ def generate_html_table(content):
     return table_html
 
 
-def query_database_for_table_content(table_name, number=20):
+def query_database_for_table_content(engine, table_name, number=20):
     # Initialize content list
     content_list = []
+    # Create a connection from the engine
+    conn = engine.connect()
+    insp = inspect(engine)
 
     # Get the schema of the table
-    schema = getTableSchema(table_name)
-    # Query the database to get the content of the table
-    sql_content = text(f"""SELECT * FROM {schema}.{table_name} LIMIT {number};""")
-    result = db.session.execute(sql_content, {'table_name': table_name, 'number': number}).fetchall()
-    
+    if engine.dialect.name == 'postgresql':
+        schema = getTableSchema(table_name) 
+        # Query the database to get the content of the table
+        sql_content = f"SELECT * FROM {schema}.{table_name} LIMIT :number"
+    else:
+        schema = insp.default_schema_name
+        sql_content = f"SELECT * FROM {table_name} LIMIT :number"
+        
+    result = conn.execute(text(sql_content), {'table_name': table_name, 'number': number}).fetchall()
+    conn.close()    # Close the connection
+
     if not result:
         return []
-    
-    # Get the column names
-    sql_columns = text("""
-            SELECT column_name
-            FROM information_schema.columns
-            WHERE table_name = :table_name;
-        """)
-    column_names = db.session.execute(sql_columns, {'table_name': table_name}).fetchall()
-
-    # Prepare column names
-    columns = [column_name[0] for column_name in column_names]
+
+    # Get the column names using the inspector
+    columns = [col['name'] for col in insp.get_columns(table_name, schema)]
     content_list.append(columns)
 
     # Append rows to content list
@@ -186,7 +266,7 @@ def query_database_for_table_content(table_name, number=20):
         content_list.append(list(row))
 
     return content_list
-
+    
 # Only postgresql needs this function (database_type = 'postgresql')
 def getTableSchema(table_name):
     sql= text(f"""
@@ -206,6 +286,29 @@ def getSchema(insp):
     return schemas
 
 
+def getTableInstance(engine, table_name):
+    insp = inspect(engine)
+    table = importMetadata(engine, None, [table_name], False)[table_name]
+    return table
+
+
+def showDistinctValues(engine, table_name, column_name):
+    insp = inspect(engine)
+    conn = engine.connect()
+
+    if insp.dialect.name == 'postgresql':
+        schema = getTableSchema(table_name) 
+        sql = f"SELECT DISTINCT {column_name} FROM {schema}.{table_name}"
+    else:
+        sql = f"SELECT DISTINCT {column_name} FROM {table_name}"
+    
+    result = conn.execute(text(sql))
+    names = [row[0] for row in result.fetchall()] if result != None else [] 
+    conn.close()
+
+    return names
+
+
 def importMetadata(engine, schema=None, tables_selected=None, show_all=False):
     tables = {}
     if engine == None:
diff --git a/my_flask_app/models/__pycache__/models.cpython-39.pyc b/my_flask_app/models/__pycache__/models.cpython-39.pyc
index b9d512653cb5762711304af41da98b06d94de3e5..00c03fc284ad7a2156ca2601ed3f7e504a3b2388 100644
Binary files a/my_flask_app/models/__pycache__/models.cpython-39.pyc and b/my_flask_app/models/__pycache__/models.cpython-39.pyc differ
diff --git a/my_flask_app/models/models.py b/my_flask_app/models/models.py
index 20e0597b5325d166663580d76161f812e8f37ee7..4fe07bf4e5843fe35bcf3e6c17a1e37ac3127e85 100644
--- a/my_flask_app/models/models.py
+++ b/my_flask_app/models/models.py
@@ -1,19 +1,51 @@
+from sqlalchemy import ARRAY, BIGINT, BOOLEAN, DOUBLE_PRECISION, FLOAT, INTEGER, JSON, NUMERIC, SMALLINT, TIMESTAMP, UUID, VARCHAR, MetaData, String, create_engine, text, inspect
 from sqlalchemy.types import UserDefinedType
 import re
 
 
-class CompressedDataType(UserDefinedType):
-    def get_col_spec(self):
-        return "_timescaledb_internal.compressed_data"
+class Observation_Spec:
+    def __init__(self, type:str, label:str, features_name:list = None):
+        self.type = type    # 'event' or 'measurement'
+        self.label = label
+        self.features_name = features_name if features_name else []
+
+    def add_feature(self, name):
+        self.features_name.append(name)
+
+    def remove_feature(self, name):
+        self.features_name.remove(name)
+
+class Obesrvation(Observation_Spec):
+    def __init__(self, time, type, object, label, features_name:list = None, feature:dict = None):
+        super().__init__(type, label, features_name=None)
+        self.time = time
+        self.object = object
+        self.features_dict = {}
+        if features_name is not None:
+            for name in features_name:
+                if feature[name] is not None:
+                    self.features_dict[name] = feature[name]
+                self.features_dict[name] = ''
+
+    def add_feature(self, feature_name, feature_value):
+        self.features[feature_name] = feature_value
+
+# class Segment_Spec:
+#     def __init__(self, type, label, features=None):
+#         self.type = type    # 'event' or 'measurement'
+#         self.label = label
+#         self.features = features if features else {}
+
+#     def add_feature(self, feature_name):
+#         self.features[feature_name] = feature_value
+
+# class Segment(Segment_Spec):
+#     def __init__(self, time, type, object, label, features=None):
+#         super().__init__(type, label, features=None)
+#         self.time = time
+#         self.object = object
 
-class RegType(UserDefinedType):
-    def get_col_spec(self):
-        return "regtype"
 
-class RegRole(UserDefinedType):
-    def get_col_spec(self):
-        return "regrole"
-    
 
 class Theme:
     def __init__(self, color, fillcolor, fillcolorC,
@@ -100,6 +132,28 @@ class CustomTable():
         return s
 
 
+    def to_dict(self):
+        return {
+            'name': self.name,
+            'schema': self.schema,
+            'label': self.label,
+            'columns': [column.to_dict() for column in self.columns],
+            'uniques': self.uniques,
+            'pks': self.pks,
+            'fks': {k: [col.to_dict() for col in v] for k, v in self.fks.items()},
+        }
+
+    @staticmethod
+    def from_dict(data):
+        table = CustomTable(data['name'], data['schema'])
+        table.label = data['label']
+        table.columns = [CustomColumn.from_dict(col_data) for col_data in data['columns']]
+        table.uniques = data['uniques']
+        table.pks = data['pks']
+        table.fks = {k: [CustomColumn.from_dict(col_data) for col_data in v] for k, v in data['fks'].items()}
+        return table
+
+
 class CustomColumn:
     def __init__(self, table, name, comment):
         self.table = table
@@ -147,4 +201,45 @@ class CustomColumn:
                 self.datatype += f"({str(datatype['precision'])},{str(datatype['scale'])})"
                 #if column.datatype.startswith("NUMBER("):
                 #    column.datatype = f"FLOAT({str(datatype['precision'])},{str(datatype['scale'])})"
-        self.datatype = self.datatype.lower()
\ No newline at end of file
+        self.datatype = self.datatype.lower()
+
+    def to_dict(self):
+        return {
+            'name': self.name,
+            'comment': self.comment,
+            'nullable': self.nullable,
+            'datatype': str(self.datatype),  # Convert datatype to string if it's not a basic type
+            'identity': self.identity,
+            'isunique': self.isunique,
+            'ispk': self.ispk,
+            'pkconstraint': self.pkconstraint,
+            'fkof': self.fkof.name if self.fkof else None,
+        }
+    
+    @staticmethod
+    def from_dict(data):
+        column = CustomColumn(None, data['name'], data['comment'])
+        column.nullable = data['nullable']
+        column.datatype = data['datatype']
+        column.identity = data['identity']
+        column.isunique = data['isunique']
+        column.ispk = data['ispk']
+        column.pkconstraint = data['pkconstraint']
+        # Handle 'fkof' carefully to avoid circular references
+        column.fkof = CustomColumn.from_dict(data['fkof']) if data['fkof'] else None
+        # column.fkof = all_columns.get(data['fkof'])
+        return column
+    
+
+class CompressedDataType(UserDefinedType):
+    def get_col_spec(self):
+        return "_timescaledb_internal.compressed_data"
+
+class RegType(UserDefinedType):
+    def get_col_spec(self):
+        return "regtype"
+
+class RegRole(UserDefinedType):
+    def get_col_spec(self):
+        return "regrole"
+    
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/LICENSE b/my_flask_app/static/choices.js/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..432f18873762b3f277abea8da35cd97982cedc6a
--- /dev/null
+++ b/my_flask_app/static/choices.js/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Josh Johnson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/my_flask_app/static/choices.js/README.md b/my_flask_app/static/choices.js/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4f49648b8e9c66569b3b09196524ba743a810fe1
--- /dev/null
+++ b/my_flask_app/static/choices.js/README.md
@@ -0,0 +1,1169 @@
+# Choices.js [![Actions Status](https://github.com/jshjohnson/Choices/workflows/Build%20and%20test/badge.svg)](https://github.com/jshjohnson/Choices/actions) [![Actions Status](https://github.com/jshjohnson/Choices/workflows/Bundle%20size%20checks/badge.svg)](https://github.com/jshjohnson/Choices/actions) [![npm](https://img.shields.io/npm/v/choices.js.svg)](https://www.npmjs.com/package/choices.js)
+
+A vanilla, lightweight (~19kb gzipped 🎉), configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.
+
+[Demo](https://choices-js.github.io/Choices/)
+
+## TL;DR
+
+- Lightweight
+- No jQuery dependency
+- Configurable sorting
+- Flexible styling
+- Fast search/filtering
+- Clean API
+- Right-to-left support
+- Custom templates
+
+---
+
+### Interested in writing your own ES6 JavaScript plugins? Check out [ES6.io](https://ES6.io/friend/JOHNSON) for great tutorials! 💪🏼
+
+### Sponsored by:
+<p align="center">
+  <a href="https://wanderermaps.com/" target="_blank" rel="noopener noreferrer">
+    <img src="https://cdn.shopify.com/s/files/1/0614/3357/7715/files/Logo_BlackWithBackground_200x.png?v=1644802773" alt="Wanderer Maps logo">
+  </a>
+</p>
+
+---
+
+## Table of Contents
+
+- [Installation](#installation)
+- [Setup](#setup)
+- [Terminology](#terminology)
+- [Input Types](#input-types)
+- [Configuration Options](#configuration-options)
+- [Callbacks](#callbacks)
+- [Events](#events)
+- [Methods](#methods)
+- [Development](#development)
+- [License](#license)
+
+## Installation
+
+With [NPM](https://www.npmjs.com/package/choices.js):
+
+```zsh
+npm install choices.js
+```
+
+With [Yarn](https://yarnpkg.com/):
+
+```zsh
+yarn add choices.js
+```
+
+From a [CDN](https://www.jsdelivr.com/package/npm/choices.js):
+
+**Note:** There is sometimes a delay before the latest version of Choices is reflected on the CDN.
+
+```html
+<!-- Include base CSS (optional) -->
+<link
+  rel="stylesheet"
+  href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/base.min.css"
+/>
+<!-- Or versioned -->
+<link
+  rel="stylesheet"
+  href="https://cdn.jsdelivr.net/npm/choices.js@9.0.1/public/assets/styles/base.min.css"
+/>
+
+<!-- Include Choices CSS -->
+<link
+  rel="stylesheet"
+  href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"
+/>
+<!-- Or versioned -->
+<link
+  rel="stylesheet"
+  href="https://cdn.jsdelivr.net/npm/choices.js@9.0.1/public/assets/styles/choices.min.css"
+/>
+
+<!-- Include Choices JavaScript (latest) -->
+<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
+<!-- Or versioned -->
+<script src="https://cdn.jsdelivr.net/npm/choices.js@9.0.1/public/assets/scripts/choices.min.js"></script>
+```
+
+Or include Choices directly:
+
+```html
+<!-- Include base CSS (optional) -->
+<link rel="stylesheet" href="public/assets/styles/base.min.css" />
+<!-- Include Choices CSS -->
+<link rel="stylesheet" href="public/assets/styles/choices.min.css" />
+<!-- Include Choices JavaScript -->
+<script src="/public/assets/scripts/choices.min.js"></script>
+```
+
+## Setup
+
+**Note:** If you pass a selector which targets multiple elements, the first matching element will be used. Versions prior to 8.x.x would return multiple Choices instances.
+
+```js
+  // Pass single element
+  const element = document.querySelector('.js-choice');
+  const choices = new Choices(element);
+
+  // Pass reference
+  const choices = new Choices('[data-trigger]');
+  const choices = new Choices('.js-choice');
+
+  // Pass jQuery element
+  const choices = new Choices($('.js-choice')[0]);
+
+  // Passing options (with default options)
+  const choices = new Choices(element, {
+    silent: false,
+    items: [],
+    choices: [],
+    renderChoiceLimit: -1,
+    maxItemCount: -1,
+    addItems: true,
+    addItemFilter: null,
+    removeItems: true,
+    removeItemButton: false,
+    editItems: false,
+    allowHTML: true,
+    duplicateItemsAllowed: true,
+    delimiter: ',',
+    paste: true,
+    searchEnabled: true,
+    searchChoices: true,
+    searchFloor: 1,
+    searchResultLimit: 4,
+    searchFields: ['label', 'value'],
+    position: 'auto',
+    resetScrollPosition: true,
+    shouldSort: true,
+    shouldSortItems: false,
+    sorter: () => {...},
+    placeholder: true,
+    placeholderValue: null,
+    searchPlaceholderValue: null,
+    prependValue: null,
+    appendValue: null,
+    renderSelectedChoices: 'auto',
+    loadingText: 'Loading...',
+    noResultsText: 'No results found',
+    noChoicesText: 'No choices to choose from',
+    itemSelectText: 'Press to select',
+    uniqueItemText: 'Only unique values can be added',
+    customAddItemText: 'Only values matching specific conditions can be added',
+    addItemText: (value) => {
+      return `Press Enter to add <b>"${value}"</b>`;
+    },
+    maxItemText: (maxItemCount) => {
+      return `Only ${maxItemCount} values can be added`;
+    },
+    valueComparer: (value1, value2) => {
+      return value1 === value2;
+    },
+    classNames: {
+      containerOuter: 'choices',
+      containerInner: 'choices__inner',
+      input: 'choices__input',
+      inputCloned: 'choices__input--cloned',
+      list: 'choices__list',
+      listItems: 'choices__list--multiple',
+      listSingle: 'choices__list--single',
+      listDropdown: 'choices__list--dropdown',
+      item: 'choices__item',
+      itemSelectable: 'choices__item--selectable',
+      itemDisabled: 'choices__item--disabled',
+      itemChoice: 'choices__item--choice',
+      placeholder: 'choices__placeholder',
+      group: 'choices__group',
+      groupHeading: 'choices__heading',
+      button: 'choices__button',
+      activeState: 'is-active',
+      focusState: 'is-focused',
+      openState: 'is-open',
+      disabledState: 'is-disabled',
+      highlightedState: 'is-highlighted',
+      selectedState: 'is-selected',
+      flippedState: 'is-flipped',
+      loadingState: 'is-loading',
+      noResults: 'has-no-results',
+      noChoices: 'has-no-choices'
+    },
+    // Choices uses the great Fuse library for searching. You
+    // can find more options here: https://fusejs.io/api/options.html
+    fuseOptions: {
+      includeScore: true
+    },
+    labelId: '',
+    callbackOnInit: null,
+    callbackOnCreateTemplates: null
+  });
+```
+
+## Terminology
+
+| Word   | Definition                                                                                                                                                                                                                                                                                                              |
+| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Choice | A choice is a value a user can select. A choice would be equivalent to the `<option></option>` element within a select input.                                                                                                                                                                                           |
+| Group  | A group is a collection of choices. A group should be seen as equivalent to a `<optgroup></optgroup>` element within a select input.                                                                                                                                                                                    |
+| Item   | An item is an inputted value (text input) or a selected choice (select element). In the context of a select element, an item is equivalent to a selected option element: `<option value="Hello" selected></option>` whereas in the context of a text input an item is equivalent to `<input type="text" value="Hello">` |
+
+## Input Types
+
+Choices works with the following input types, referenced in the documentation as noted.
+
+| HTML Element                                                                                           | Documentation "Input Type" |
+| -------------------------------------------------------------------------------------------------------| -------------------------- |
+| [`<input type="text">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)               | `text`                     |
+| [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)                         | `select-one`               |
+| [`<select multiple>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#attr-multiple)  | `select-multiple`          |
+
+## Configuration Options
+
+### silent
+
+**Type:** `Boolean` **Default:** `false`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Optionally suppress console errors and warnings.
+
+### items
+
+**Type:** `Array` **Default:** `[]`
+
+**Input types affected:** `text`
+
+**Usage:** Add pre-selected items (see terminology) to text input.
+
+Pass an array of strings:
+
+`['value 1', 'value 2', 'value 3']`
+
+Pass an array of objects:
+
+```
+[{
+  value: 'Value 1',
+  label: 'Label 1',
+  id: 1
+},
+{
+  value: 'Value 2',
+  label: 'Label 2',
+  id: 2,
+  customProperties: {
+    random: 'I am a custom property'
+  }
+}]
+```
+
+### choices
+
+**Type:** `Array` **Default:** `[]`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Add choices (see terminology) to select input.
+
+Pass an array of objects:
+
+```
+[{
+  value: 'Option 1',
+  label: 'Option 1',
+  selected: true,
+  disabled: false,
+},
+{
+  value: 'Option 2',
+  label: 'Option 2',
+  selected: false,
+  disabled: true,
+  customProperties: {
+    description: 'Custom description about Option 2',
+    random: 'Another random custom property'
+  },
+}]
+```
+
+### renderChoiceLimit
+
+**Type:** `Number` **Default:** `-1`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** The amount of choices to be rendered within the dropdown list ("-1" indicates no limit). This is useful if you have a lot of choices where it is easier for a user to use the search area to find a choice.
+
+### maxItemCount
+
+**Type:** `Number` **Default:** `-1`
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** The amount of items a user can input/select ("-1" indicates no limit).
+
+### addItems
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `text`
+
+**Usage:** Whether a user can add items.
+
+### removeItems
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Whether a user can remove items.
+
+### removeItemButton
+
+**Type:** `Boolean` **Default:** `false`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Whether each item should have a remove button.
+
+### editItems
+
+**Type:** `Boolean` **Default:** `false`
+
+**Input types affected:** `text`
+
+**Usage:** Whether a user can edit items. An item's value can be edited by pressing the backspace.
+
+### allowHTML
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Whether HTML should be rendered in all Choices elements. If `false`, all elements (placeholder, items, etc.) will be treated as plain text. If `true`, this can be used to perform XSS scripting attacks if you load choices from a remote source.
+
+**Deprecation Warning:** This will default to `false` in a future release.
+
+### duplicateItemsAllowed
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Whether duplicate inputted/chosen items are allowed
+
+### delimiter
+
+**Type:** `String` **Default:** `,`
+
+**Input types affected:** `text`
+
+**Usage:** What divides each value. The default delimiter separates each value with a comma: `"Value 1, Value 2, Value 3"`.
+
+### paste
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Whether a user can paste into the input.
+
+### searchEnabled
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `select-one`
+
+**Usage:** Whether a search area should be shown. **Note:** Multiple select boxes will _always_ show search areas.
+
+### searchChoices
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `select-one`
+
+**Usage:** Whether choices should be filtered by input or not. If `false`, the search event will still emit, but choices will not be filtered.
+
+### searchFields
+
+**Type:** `Array/String` **Default:** `['label', 'value']`
+
+**Input types affected:**`select-one`, `select-multiple`
+
+**Usage:** Specify which fields should be used when a user is searching. If you have added custom properties to your choices, you can add these values thus: `['label', 'value', 'customProperties.example']`.
+
+### searchFloor
+
+**Type:** `Number` **Default:** `1`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** The minimum length a search value should be before choices are searched.
+
+### searchResultLimit: 4,
+
+**Type:** `Number` **Default:** `4`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** The maximum amount of search results to show.
+
+### position
+
+**Type:** `String` **Default:** `auto`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Whether the dropdown should appear above (`top`) or below (`bottom`) the input. By default, if there is not enough space within the window the dropdown will appear above the input, otherwise below it.
+
+### resetScrollPosition
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `select-multiple`
+
+**Usage:** Whether the scroll position should reset after adding an item.
+
+### addItemFilter
+
+**Type:** `string | RegExp | Function` **Default:** `null`
+
+**Input types affected:** `text`
+
+**Usage:** A RegExp or string (will be passed to RegExp constructor internally) or filter function that will need to return `true` for a user to successfully add an item.
+
+**Example:**
+
+```js
+// Only adds items matching the text test
+new Choices(element, {
+  addItemFilter: (value) => {
+    return ['orange', 'apple', 'banana'].includes(value);
+  };
+});
+
+// only items ending to `-red`
+new Choices(element, {
+  addItemFilter: '-red$';
+});
+
+```
+
+### shouldSort
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Whether choices and groups should be sorted. If false, choices/groups will appear in the order they were given.
+
+### shouldSortItems
+
+**Type:** `Boolean` **Default:** `false`
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Whether items should be sorted. If false, items will appear in the order they were selected.
+
+### sorter
+
+**Type:** `Function` **Default:** sortByAlpha
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** The function that will sort choices and items before they are displayed (unless a user is searching). By default choices and items are sorted by alphabetical order.
+
+**Example:**
+
+```js
+// Sorting via length of label from largest to smallest
+const example = new Choices(element, {
+  sorter: function(a, b) {
+    return b.label.length - a.label.length;
+  },
+};
+```
+
+### placeholder
+
+**Type:** `Boolean` **Default:** `true`
+
+**Input types affected:** `text`
+
+**Usage:** Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value.
+
+**Note:** For select boxes, the recommended way of adding a placeholder is as follows:
+
+```html
+<select>
+  <option value="">This is a placeholder</option>
+  <option>...</option>
+  <option>...</option>
+  <option>...</option>
+</select>
+```
+
+For backward compatibility, `<option placeholder>This is a placeholder</option>` is also supported.
+
+### placeholderValue
+
+**Type:** `String` **Default:** `null`
+
+**Input types affected:** `text`
+
+**Usage:** The value of the inputs placeholder.
+
+### searchPlaceholderValue
+
+**Type:** `String` **Default:** `null`
+
+**Input types affected:** `select-one`
+
+**Usage:** The value of the search inputs placeholder.
+
+### prependValue
+
+**Type:** `String` **Default:** `null`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Prepend a value to each item added/selected.
+
+### appendValue
+
+**Type:** `String` **Default:** `null`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Append a value to each item added/selected.
+
+### renderSelectedChoices
+
+**Type:** `String` **Default:** `auto`
+
+**Input types affected:** `select-multiple`
+
+**Usage:** Whether selected choices should be removed from the list. By default choices are removed when they are selected in multiple select box. To always render choices pass `always`.
+
+### loadingText
+
+**Type:** `String` **Default:** `Loading...`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** The text that is shown whilst choices are being populated via AJAX.
+
+### noResultsText
+
+**Type:** `String/Function` **Default:** `No results found`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** The text that is shown when a user's search has returned no results. Optionally pass a function returning a string.
+
+### noChoicesText
+
+**Type:** `String/Function` **Default:** `No choices to choose from`
+
+**Input types affected:** `select-multiple`
+
+**Usage:** The text that is shown when a user has selected all possible choices. Optionally pass a function returning a string.
+
+### itemSelectText
+
+**Type:** `String` **Default:** `Press to select`
+
+**Input types affected:** `select-multiple`, `select-one`
+
+**Usage:** The text that is shown when a user hovers over a selectable choice.
+
+### addItemText
+
+**Type:** `String/Function` **Default:** `Press Enter to add "${value}"`
+
+**Input types affected:** `text`
+
+**Usage:** The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example), otherwise pass a string.
+
+### maxItemText
+
+**Type:** `String/Function` **Default:** `Only ${maxItemCount} values can be added`
+
+**Input types affected:** `text`
+
+**Usage:** The text that is shown when a user has focus on the input but has already reached the [max item count](https://github.com/jshjohnson/Choices#maxitemcount). To access the max item count, pass a function with a `maxItemCount` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example), otherwise pass a string.
+
+### valueComparer
+
+**Type:** `Function` **Default:** `strict equality`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** A custom compare function used when finding choices by value (using `setChoiceByValue`).
+
+**Example:**
+
+```js
+const example = new Choices(element, {
+  valueComparer: (a, b) => value.trim() === b.trim(),
+};
+```
+
+### labelId
+
+**Type:** `String` **Default:** ``
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** The labelId improves accessibility. If set, it will add aria-labelledby to the choices element.
+
+### classNames
+
+**Type:** `Object` **Default:**
+
+```
+classNames: {
+  containerOuter: 'choices',
+  containerInner: 'choices__inner',
+  input: 'choices__input',
+  inputCloned: 'choices__input--cloned',
+  list: 'choices__list',
+  listItems: 'choices__list--multiple',
+  listSingle: 'choices__list--single',
+  listDropdown: 'choices__list--dropdown',
+  item: 'choices__item',
+  itemSelectable: 'choices__item--selectable',
+  itemDisabled: 'choices__item--disabled',
+  itemOption: 'choices__item--choice',
+  group: 'choices__group',
+  groupHeading : 'choices__heading',
+  button: 'choices__button',
+  activeState: 'is-active',
+  focusState: 'is-focused',
+  openState: 'is-open',
+  disabledState: 'is-disabled',
+  highlightedState: 'is-highlighted',
+  selectedState: 'is-selected',
+  flippedState: 'is-flipped',
+  selectedState: 'is-highlighted',
+}
+```
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Classes added to HTML generated by Choices. By default classnames follow the [BEM](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/) notation.
+
+## Callbacks
+
+**Note:** For each callback, `this` refers to the current instance of Choices. This can be useful if you need access to methods (`this.disable()`) or the config object (`this.config`).
+
+### callbackOnInit
+
+**Type:** `Function` **Default:** `null`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Function to run once Choices initialises.
+
+### callbackOnCreateTemplates
+
+**Type:** `Function` **Default:** `null` **Arguments:** `template`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Function to run on template creation. Through this callback it is possible to provide custom templates for the various components of Choices (see terminology). For Choices to work with custom templates, it is important you maintain the various data attributes defined [here](https://github.com/Choices-js/Choices/blob/master/src/scripts/templates.ts).
+If you want just extend a little original template then you may use `Choices.defaults.templates` to get access to
+original template function.
+
+Templates receive the full Choices config as the first argument to any template, which allows you to conditionally display things based on the options specified.
+
+**Example:**
+
+```js
+const example = new Choices(element, {
+  callbackOnCreateTemplates: () => ({
+    input: (...args) =>
+      Object.assign(Choices.defaults.templates.input.call(this, ...args), {
+        type: 'email',
+      }),
+  }),
+});
+```
+
+or more complex:
+
+```js
+const example = new Choices(element, {
+  callbackOnCreateTemplates: function(template) {
+    return {
+      item: ({ classNames }, data) => {
+        return template(`
+          <div class="${classNames.item} ${
+          data.highlighted
+            ? classNames.highlightedState
+            : classNames.itemSelectable
+        } ${
+          data.placeholder ? classNames.placeholder : ''
+        }" data-item data-id="${data.id}" data-value="${data.value}" ${
+          data.active ? 'aria-selected="true"' : ''
+        } ${data.disabled ? 'aria-disabled="true"' : ''}>
+            <span>&bigstar;</span> ${data.label}
+          </div>
+        `);
+      },
+      choice: ({ classNames }, data) => {
+        return template(`
+          <div class="${classNames.item} ${classNames.itemChoice} ${
+          data.disabled ? classNames.itemDisabled : classNames.itemSelectable
+        }" data-select-text="${this.config.itemSelectText}" data-choice ${
+          data.disabled
+            ? 'data-choice-disabled aria-disabled="true"'
+            : 'data-choice-selectable'
+        } data-id="${data.id}" data-value="${data.value}" ${
+          data.groupId > 0 ? 'role="treeitem"' : 'role="option"'
+        }>
+            <span>&bigstar;</span> ${data.label}
+          </div>
+        `);
+      },
+    };
+  },
+});
+```
+
+## Events
+
+**Note:** Events fired by Choices behave the same as standard events. Each event is triggered on the element passed to Choices (accessible via `this.passedElement`. Arguments are accessible within the `event.detail` object.
+
+**Example:**
+
+```js
+const element = document.getElementById('example');
+const example = new Choices(element);
+
+element.addEventListener(
+  'addItem',
+  function(event) {
+    // do something creative here...
+    console.log(event.detail.id);
+    console.log(event.detail.value);
+    console.log(event.detail.label);
+    console.log(event.detail.customProperties);
+    console.log(event.detail.groupValue);
+  },
+  false,
+);
+
+// or
+const example = new Choices(document.getElementById('example'));
+
+example.passedElement.element.addEventListener(
+  'addItem',
+  function(event) {
+    // do something creative here...
+    console.log(event.detail.id);
+    console.log(event.detail.value);
+    console.log(event.detail.label);
+    console.log(event.detail.customProperties);
+    console.log(event.detail.groupValue);
+  },
+  false,
+);
+```
+
+### addItem
+
+**Payload:** `id, value, label, customProperties, groupValue, keyCode`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Triggered each time an item is added (programmatically or by the user).
+
+### removeItem
+
+**Payload:** `id, value, label, customProperties, groupValue`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Triggered each time an item is removed (programmatically or by the user).
+
+### highlightItem
+
+**Payload:** `id, value, label, groupValue`
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Triggered each time an item is highlighted.
+
+### unhighlightItem
+
+**Payload:** `id, value, label, groupValue`
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Triggered each time an item is unhighlighted.
+
+### choice
+
+**Payload:** `choice`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Triggered each time a choice is selected **by a user**, regardless if it changes the value of the input.
+`choice` is a Choice object here (see terminology or typings file)
+
+### change
+
+**Payload:** `value`
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Triggered each time an item is added/removed **by a user**.
+
+### search
+
+**Payload:** `value`, `resultCount`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Triggered when a user types into an input to search choices.
+
+### showDropdown
+
+**Payload:** -
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Triggered when the dropdown is shown.
+
+### hideDropdown
+
+**Payload:** -
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Triggered when the dropdown is hidden.
+
+### highlightChoice
+
+**Payload:** `el`
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Triggered when a choice from the dropdown is highlighted.
+The `el` argument is choices.passedElement object that was affected.
+
+## Methods
+
+Methods can be called either directly or by chaining:
+
+```js
+// Calling a method by chaining
+const choices = new Choices(element, {
+  addItems: false,
+  removeItems: false,
+})
+  .setValue(['Set value 1', 'Set value 2'])
+  .disable();
+
+// Calling a method directly
+const choices = new Choices(element, {
+  addItems: false,
+  removeItems: false,
+});
+
+choices.setValue(['Set value 1', 'Set value 2']);
+choices.disable();
+```
+
+### destroy();
+
+**Input types affected:** `text`, `select-multiple`, `select-one`
+
+**Usage:** Kills the instance of Choices, removes all event listeners and returns passed input to its initial state.
+
+### init();
+
+**Input types affected:** `text`, `select-multiple`, `select-one`
+
+**Usage:** Creates a new instance of Choices, adds event listeners, creates templates and renders a Choices element to the DOM.
+
+**Note:** This is called implicitly when a new instance of Choices is created. This would be used after a Choices instance had already been destroyed (using `destroy()`).
+
+### highlightAll();
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Highlight each chosen item (selected items can be removed).
+
+### unhighlightAll();
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Un-highlight each chosen item.
+
+### removeActiveItemsByValue(value);
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Remove each item by a given value.
+
+### removeActiveItems(excludedId);
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Remove each selectable item.
+
+### removeHighlightedItems();
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Remove each item the user has selected.
+
+### showDropdown();
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Show option list dropdown (only affects select inputs).
+
+### hideDropdown();
+
+**Input types affected:** `text`, `select-multiple`
+
+**Usage:** Hide option list dropdown (only affects select inputs).
+
+### setChoices(choices, value, label, replaceChoices);
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Set choices of select input via an array of objects (or function that returns array of object or promise of it), a value field name and a label field name.
+
+This behaves the similar as passing items via the `choices` option but can be called after initialising Choices. This can also be used to add groups of choices (see example 3); Optionally pass a true `replaceChoices` value to remove any existing choices. Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). Passing an empty array as the first parameter, and a true `replaceChoices` is the same as calling `clearChoices` (see below).
+
+**Example 1:**
+
+```js
+const example = new Choices(element);
+
+example.setChoices(
+  [
+    { value: 'One', label: 'Label One', disabled: true },
+    { value: 'Two', label: 'Label Two', selected: true },
+    { value: 'Three', label: 'Label Three' },
+  ],
+  'value',
+  'label',
+  false,
+);
+```
+
+**Example 2:**
+
+```js
+const example = new Choices(element);
+
+// Passing a function that returns Promise of choices
+example.setChoices(async () => {
+  try {
+    const items = await fetch('/items');
+    return items.json();
+  } catch (err) {
+    console.error(err);
+  }
+});
+```
+
+**Example 3:**
+
+```js
+const example = new Choices(element);
+
+example.setChoices(
+  [
+    {
+      label: 'Group one',
+      id: 1,
+      disabled: false,
+      choices: [
+        { value: 'Child One', label: 'Child One', selected: true },
+        { value: 'Child Two', label: 'Child Two', disabled: true },
+        { value: 'Child Three', label: 'Child Three' },
+      ],
+    },
+    {
+      label: 'Group two',
+      id: 2,
+      disabled: false,
+      choices: [
+        { value: 'Child Four', label: 'Child Four', disabled: true },
+        { value: 'Child Five', label: 'Child Five' },
+        {
+          value: 'Child Six',
+          label: 'Child Six',
+          customProperties: {
+            description: 'Custom description about child six',
+            random: 'Another random custom property',
+          },
+        },
+      ],
+    },
+  ],
+  'value',
+  'label',
+  false,
+);
+```
+
+### clearChoices();
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Clear all choices from select
+
+### getValue(valueOnly)
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Get value(s) of input (i.e. inputted items (text) or selected choices (select)). Optionally pass an argument of `true` to only return values rather than value objects.
+
+**Example:**
+
+```js
+const example = new Choices(element);
+const values = example.getValue(true); // returns ['value 1', 'value 2'];
+const valueArray = example.getValue(); // returns [{ active: true, choiceId: 1, highlighted: false, id: 1, label: 'Label 1', value: 'Value 1'},  { active: true, choiceId: 2, highlighted: false, id: 2, label: 'Label 2', value: 'Value 2'}];
+```
+
+### setValue(items);
+
+**Input types affected:** `text`
+
+**Usage:** Set value of input based on an array of objects or strings. This behaves exactly the same as passing items via the `items` option but can be called after initialising Choices.
+
+**Example:**
+
+```js
+const example = new Choices(element);
+
+// via an array of objects
+example.setValue([
+  { value: 'One', label: 'Label One' },
+  { value: 'Two', label: 'Label Two' },
+  { value: 'Three', label: 'Label Three' },
+]);
+
+// or via an array of strings
+example.setValue(['Four', 'Five', 'Six']);
+```
+
+### setChoiceByValue(value);
+
+**Input types affected:** `select-one`, `select-multiple`
+
+**Usage:** Set value of input based on existing Choice. `value` can be either a single string or an array of strings
+
+**Example:**
+
+```js
+const example = new Choices(element, {
+  choices: [
+    { value: 'One', label: 'Label One' },
+    { value: 'Two', label: 'Label Two', disabled: true },
+    { value: 'Three', label: 'Label Three' },
+  ],
+});
+
+example.setChoiceByValue('Two'); // Choice with value of 'Two' has now been selected.
+```
+
+### clearStore();
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Removes all items, choices and groups. Use with caution.
+
+### clearInput();
+
+**Input types affected:** `text`
+
+**Usage:** Clear input of any user inputted text.
+
+### disable();
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Disables input from accepting new value/selecting further choices.
+
+### enable();
+
+**Input types affected:** `text`, `select-one`, `select-multiple`
+
+**Usage:** Enables input to accept new values/select further choices.
+
+## Browser compatibility
+
+Choices is compiled using [Babel](https://babeljs.io/) targeting browsers [with more than 1% of global usage](https://github.com/jshjohnson/Choices/blob/master/.browserslistrc) and expecting that features [listed below](https://github.com/jshjohnson/Choices/blob/master/.eslintrc.json#L62) are available or polyfilled in browser.
+You may see exact list of target browsers by running `npx browserslist` within this repository folder.
+If you need to support a browser that does not have one of the features listed below,
+I suggest including a polyfill from the very good [polyfill.io](https://polyfill.io/v3/):
+
+**Polyfill example used for the demo:**
+
+```html
+<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=Array.from%2Ces5%2Ces6%2CSymbol%2CSymbol.iterator%2CDOMTokenList%2CObject.assign%2CCustomEvent%2CElement.prototype.classList%2CElement.prototype.closest%2CElement.prototype.dataset%2CArray.prototype.find%2CArray.prototype.includes"></script>
+```
+
+**Features used in Choices:**
+
+```polyfills
+Array.from
+Array.prototype.find
+Array.prototype.includes
+Symbol
+Symbol.iterator
+DOMTokenList
+Object.assign
+CustomEvent
+Element.prototype.classList
+Element.prototype.closest
+Element.prototype.dataset
+```
+
+## Development
+
+To setup a local environment: clone this repo, navigate into its directory in a terminal window and run the following command:
+
+`npm install`
+
+### NPM tasks
+
+| Task                      | Usage                                                        |
+| ------------------------- | ------------------------------------------------------------ |
+| `npm run start`           | Fire up local server for development                         |
+| `npm run test:unit`       | Run sequence of tests once                                   |
+| `npm run test:unit:watch` | Fire up test server and re-test on file change               |
+| `npm run test:e2e`        | Run sequence of e2e tests (with local server)                |
+| `npm run test`            | Run both unit and e2e tests                                  |
+| `npm run cypress:open`    | Run Cypress e2e tests (GUI)                                  |
+| `npm run cypress:run`     | Run Cypress e2e tests (CLI)                                  |
+| `npm run js:build`        | Compile Choices to an uglified JavaScript file               |
+| `npm run css:watch`       | Watch SCSS files for changes. On a change, run build process |
+| `npm run css:build`       | Compile, minify and prefix SCSS files to CSS                 |
+
+### Interested in contributing?
+
+We're always interested in having more active maintainers.  Please get in touch if you're interested 👍
+
+## License
+
+MIT License
+
+## Web component
+
+Want to use Choices as a web component? You're in luck. Adidas have built one for their design system which can be found [here](https://github.com/adidas/choicesjs-stencil).
+
+## Misc
+
+Thanks to [@mikefrancis](https://github.com/mikefrancis/) for [sending me on a hunt](https://twitter.com/_mikefrancis/status/701797835826667520) for a non-jQuery solution for select boxes that eventually led to this being built!
diff --git a/my_flask_app/static/choices.js/package.json b/my_flask_app/static/choices.js/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c90ad7ad71eba990a9430eafacb4a45c93bbb35
--- /dev/null
+++ b/my_flask_app/static/choices.js/package.json
@@ -0,0 +1,144 @@
+{
+  "name": "choices.js",
+  "version": "10.2.0",
+  "description": "A vanilla JS customisable text input/select box plugin",
+  "main": "./public/assets/scripts/choices.js",
+  "types": "./public/types/src/index.d.ts",
+  "scripts": {
+    "start": "run-p js:watch css:watch",
+    "build": "run-p js:build css:build",
+    "lint": "run-p lint:js lint:scss",
+    "lint:js": "eslint src/scripts/**/*.ts",
+    "lint:scss": "stylelint src/**/*.scss",
+    "bundlesize": "bundlesize",
+    "cypress:run": "cypress run --browser chrome",
+    "cypress:open": "cypress open",
+    "cypress:ci": "cypress run --browser chrome --record --group $GITHUB_REF --ci-build-id $GITHUB_SHA",
+    "test": "run-s test:unit test:e2e",
+    "test:unit": "cross-env TS_NODE_TRANSPILE_ONLY=true NODE_ENV=test mocha",
+    "test:unit:watch": "npm run test:unit -- --watch --inspect=5556",
+    "test:unit:coverage": "NODE_ENV=test nyc --reporter=lcov --reporter=text --reporter=text-summary mocha",
+    "test:e2e": "run-p --race start cypress:run",
+    "js:watch": "cross-env NODE_ENV=development node server.js",
+    "js:build": "webpack --config webpack.config.prod.js",
+    "css:watch": "nodemon -e scss -x \"npm run css:build\"",
+    "css:build": "run-s css:sass css:prefix css:min",
+    "css:sass": "sass -I scss src/styles/base.scss public/assets/styles/base.css && sass -I scss src/styles/choices.scss public/assets/styles/choices.css",
+    "css:prefix": "postcss public/assets/styles/*.css --use autoprefixer --no-map --env prod --dir public/assets/styles",
+    "css:min": "csso public/assets/styles/base.css --output public/assets/styles/base.min.css && csso public/assets/styles/choices.css --output public/assets/styles/choices.min.css",
+    "deploy": "git subtree push --prefix public origin gh-pages",
+    "prepublishOnly": "npm run build"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/jshjohnson/Choices.git"
+  },
+  "author": "Josh Johnson",
+  "license": "MIT",
+  "files": [
+    "public/assets/scripts",
+    "public/assets/styles",
+    "public/types",
+    "src",
+    "!src/**/*.test.js",
+    "types"
+  ],
+  "bugs": {
+    "url": "https://github.com/jshjohnson/Choices/issues"
+  },
+  "homepage": "https://github.com/jshjohnson/Choices#readme",
+  "keywords": [
+    "customisable",
+    "input",
+    "select",
+    "vanilla",
+    "plugin",
+    "js"
+  ],
+  "devDependencies": {
+    "@babel/core": "^7.20.5",
+    "@babel/preset-env": "^7.20.2",
+    "@babel/register": "^7.18.9",
+    "@types/chai": "^4.3.4",
+    "@types/mocha": "^10.0.1",
+    "@types/sinon": "^10.0.13",
+    "@types/sinon-chai": "^3.2.9",
+    "@typescript-eslint/eslint-plugin": "^5.45.0",
+    "@typescript-eslint/parser": "^5.45.0",
+    "autoprefixer": "^10.4.13",
+    "babel-loader": "^9.1.0",
+    "bundlesize": "^0.18.1",
+    "chai": "^4.3.7",
+    "cross-env": "^7.0.3",
+    "csso-cli": "^4.0.1",
+    "cypress": "11.2.0",
+    "eslint": "^8.28.0",
+    "eslint-config-airbnb-base": "^15.0.0",
+    "eslint-config-airbnb-typescript": "^17.0.0",
+    "eslint-config-prettier": "^8.5.0",
+    "eslint-plugin-compat": "4.0.2",
+    "eslint-plugin-cypress": "^2.12.1",
+    "eslint-plugin-import": "^2.26.0",
+    "eslint-plugin-prettier": "^4.2.1",
+    "eslint-plugin-sort-class-members": "^1.15.2",
+    "eslint-webpack-plugin": "^3.2.0",
+    "express": "^4.18.2",
+    "husky": "^8.0.2",
+    "jsdom": "^20.0.3",
+    "lint-staged": "^13.0.4",
+    "mocha": "^10.1.0",
+    "nodemon": "^2.0.20",
+    "npm-run-all": "^4.1.5",
+    "nyc": "^15.1.0",
+    "postcss": "^8.4.19",
+    "postcss-cli": "^10.0.0",
+    "prettier": "^2.8.0",
+    "sass": "^1.56.1",
+    "sinon": "^15.0.0",
+    "sinon-chai": "^3.7.0",
+    "stylelint": "^14.15.0",
+    "stylelint-config-standard": "^29.0.0",
+    "stylelint-config-standard-scss": "^6.1.0",
+    "ts-loader": "^9.4.1",
+    "ts-node": "^10.9.1",
+    "typescript": "^4.9.3",
+    "webpack": "^5.75.0",
+    "webpack-cli": "^5.0.0",
+    "webpack-dev-middleware": "^6.0.1",
+    "webpack-hot-middleware": "^2.25.3"
+  },
+  "dependencies": {
+    "deepmerge": "^4.2.2",
+    "fuse.js": "^6.6.2",
+    "redux": "^4.2.0"
+  },
+  "npmName": "choices.js",
+  "npmFileMap": [
+    {
+      "files": [
+        "public/assets/scripts/*",
+        "public/assets/styles/*",
+        "public/types/*",
+        "src/icons/*"
+      ]
+    }
+  ],
+  "nyc": {
+    "include": [
+      "src/scripts/**/**/*.js"
+    ],
+    "exclude": [
+      "src/scripts/**/**/*.test.js"
+    ]
+  },
+  "bundlesize": [
+    {
+      "path": "public/assets/scripts/choices.min.js",
+      "maxSize": "25 kB"
+    },
+    {
+      "path": "public/assets/styles/choices.min.css",
+      "maxSize": "2.5 kB"
+    }
+  ]
+}
diff --git a/my_flask_app/static/choices.js/public/assets/scripts/.eslintrc.js b/my_flask_app/static/choices.js/public/assets/scripts/.eslintrc.js
new file mode 100644
index 0000000000000000000000000000000000000000..d761728aee73378d4d935cde13625df66ffe4bc6
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/scripts/.eslintrc.js
@@ -0,0 +1,27 @@
+// get polyfill settings from top level config
+// @ts-ignore
+const { settings } = require('../../../.eslintrc.json');
+
+// Adding non-polyfilable Symbol-related functions as they are most probably
+// behind the flag
+
+settings.polyfills.push(
+  'Symbol.toStringTag',
+  'Symbol.for',
+  'Object.getOwnPropertySymbols',
+  'Object.getOwnPropertyDescriptors',
+  'Promise', // Promise is gate checked
+);
+
+module.exports = /** @type {import('eslint').Linter.Config} */ ({
+  root: true,
+  extends: ['plugin:compat/recommended'],
+  parserOptions: {
+    // ensure that it's compatible with ES5 browsers, so, no `const`, etc
+    ecmaVersion: 5,
+  },
+  env: {
+    browser: true,
+  },
+  settings,
+});
diff --git a/my_flask_app/static/choices.js/public/assets/scripts/choices.js b/my_flask_app/static/choices.js/public/assets/scripts/choices.js
new file mode 100644
index 0000000000000000000000000000000000000000..8cadac64c7c8e7b43f58d26da1c57c3c0291a55c
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/scripts/choices.js
@@ -0,0 +1,6925 @@
+/*! choices.js v10.2.0 | © 2022 Josh Johnson | https://github.com/jshjohnson/Choices#readme */
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory();
+	else if(typeof define === 'function' && define.amd)
+		define([], factory);
+	else if(typeof exports === 'object')
+		exports["Choices"] = factory();
+	else
+		root["Choices"] = factory();
+})(window, function() {
+return /******/ (function() { // webpackBootstrap
+/******/ 	"use strict";
+/******/ 	var __webpack_modules__ = ({
+
+/***/ 282:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.clearChoices = exports.activateChoices = exports.filterChoices = exports.addChoice = void 0;
+var constants_1 = __webpack_require__(883);
+var addChoice = function (_a) {
+  var value = _a.value,
+    label = _a.label,
+    id = _a.id,
+    groupId = _a.groupId,
+    disabled = _a.disabled,
+    elementId = _a.elementId,
+    customProperties = _a.customProperties,
+    placeholder = _a.placeholder,
+    keyCode = _a.keyCode;
+  return {
+    type: constants_1.ACTION_TYPES.ADD_CHOICE,
+    value: value,
+    label: label,
+    id: id,
+    groupId: groupId,
+    disabled: disabled,
+    elementId: elementId,
+    customProperties: customProperties,
+    placeholder: placeholder,
+    keyCode: keyCode
+  };
+};
+exports.addChoice = addChoice;
+var filterChoices = function (results) {
+  return {
+    type: constants_1.ACTION_TYPES.FILTER_CHOICES,
+    results: results
+  };
+};
+exports.filterChoices = filterChoices;
+var activateChoices = function (active) {
+  if (active === void 0) {
+    active = true;
+  }
+  return {
+    type: constants_1.ACTION_TYPES.ACTIVATE_CHOICES,
+    active: active
+  };
+};
+exports.activateChoices = activateChoices;
+var clearChoices = function () {
+  return {
+    type: constants_1.ACTION_TYPES.CLEAR_CHOICES
+  };
+};
+exports.clearChoices = clearChoices;
+
+/***/ }),
+
+/***/ 783:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.addGroup = void 0;
+var constants_1 = __webpack_require__(883);
+var addGroup = function (_a) {
+  var value = _a.value,
+    id = _a.id,
+    active = _a.active,
+    disabled = _a.disabled;
+  return {
+    type: constants_1.ACTION_TYPES.ADD_GROUP,
+    value: value,
+    id: id,
+    active: active,
+    disabled: disabled
+  };
+};
+exports.addGroup = addGroup;
+
+/***/ }),
+
+/***/ 464:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.highlightItem = exports.removeItem = exports.addItem = void 0;
+var constants_1 = __webpack_require__(883);
+var addItem = function (_a) {
+  var value = _a.value,
+    label = _a.label,
+    id = _a.id,
+    choiceId = _a.choiceId,
+    groupId = _a.groupId,
+    customProperties = _a.customProperties,
+    placeholder = _a.placeholder,
+    keyCode = _a.keyCode;
+  return {
+    type: constants_1.ACTION_TYPES.ADD_ITEM,
+    value: value,
+    label: label,
+    id: id,
+    choiceId: choiceId,
+    groupId: groupId,
+    customProperties: customProperties,
+    placeholder: placeholder,
+    keyCode: keyCode
+  };
+};
+exports.addItem = addItem;
+var removeItem = function (id, choiceId) {
+  return {
+    type: constants_1.ACTION_TYPES.REMOVE_ITEM,
+    id: id,
+    choiceId: choiceId
+  };
+};
+exports.removeItem = removeItem;
+var highlightItem = function (id, highlighted) {
+  return {
+    type: constants_1.ACTION_TYPES.HIGHLIGHT_ITEM,
+    id: id,
+    highlighted: highlighted
+  };
+};
+exports.highlightItem = highlightItem;
+
+/***/ }),
+
+/***/ 137:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.setIsLoading = exports.resetTo = exports.clearAll = void 0;
+var constants_1 = __webpack_require__(883);
+var clearAll = function () {
+  return {
+    type: constants_1.ACTION_TYPES.CLEAR_ALL
+  };
+};
+exports.clearAll = clearAll;
+var resetTo = function (state) {
+  return {
+    type: constants_1.ACTION_TYPES.RESET_TO,
+    state: state
+  };
+};
+exports.resetTo = resetTo;
+var setIsLoading = function (isLoading) {
+  return {
+    type: constants_1.ACTION_TYPES.SET_IS_LOADING,
+    isLoading: isLoading
+  };
+};
+exports.setIsLoading = setIsLoading;
+
+/***/ }),
+
+/***/ 373:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+var __spreadArray = this && this.__spreadArray || function (to, from, pack) {
+  if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+    if (ar || !(i in from)) {
+      if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+      ar[i] = from[i];
+    }
+  }
+  return to.concat(ar || Array.prototype.slice.call(from));
+};
+var __importDefault = this && this.__importDefault || function (mod) {
+  return mod && mod.__esModule ? mod : {
+    "default": mod
+  };
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var deepmerge_1 = __importDefault(__webpack_require__(996));
+/* eslint-disable @typescript-eslint/no-explicit-any */
+var fuse_js_1 = __importDefault(__webpack_require__(221));
+var choices_1 = __webpack_require__(282);
+var groups_1 = __webpack_require__(783);
+var items_1 = __webpack_require__(464);
+var misc_1 = __webpack_require__(137);
+var components_1 = __webpack_require__(520);
+var constants_1 = __webpack_require__(883);
+var defaults_1 = __webpack_require__(789);
+var utils_1 = __webpack_require__(799);
+var reducers_1 = __webpack_require__(655);
+var store_1 = __importDefault(__webpack_require__(744));
+var templates_1 = __importDefault(__webpack_require__(686));
+/** @see {@link http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c} */
+var IS_IE11 = '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style;
+var USER_DEFAULTS = {};
+/**
+ * Choices
+ * @author Josh Johnson<josh@joshuajohnson.co.uk>
+ */
+var Choices = /** @class */function () {
+  function Choices(element, userConfig) {
+    if (element === void 0) {
+      element = '[data-choice]';
+    }
+    if (userConfig === void 0) {
+      userConfig = {};
+    }
+    var _this = this;
+    if (userConfig.allowHTML === undefined) {
+      console.warn('Deprecation warning: allowHTML will default to false in a future release. To render HTML in Choices, you will need to set it to true. Setting allowHTML will suppress this message.');
+    }
+    this.config = deepmerge_1.default.all([defaults_1.DEFAULT_CONFIG, Choices.defaults.options, userConfig],
+    // When merging array configs, replace with a copy of the userConfig array,
+    // instead of concatenating with the default array
+    {
+      arrayMerge: function (_, sourceArray) {
+        return __spreadArray([], sourceArray, true);
+      }
+    });
+    var invalidConfigOptions = (0, utils_1.diff)(this.config, defaults_1.DEFAULT_CONFIG);
+    if (invalidConfigOptions.length) {
+      console.warn('Unknown config option(s) passed', invalidConfigOptions.join(', '));
+    }
+    var passedElement = typeof element === 'string' ? document.querySelector(element) : element;
+    if (!(passedElement instanceof HTMLInputElement || passedElement instanceof HTMLSelectElement)) {
+      throw TypeError('Expected one of the following types text|select-one|select-multiple');
+    }
+    this._isTextElement = passedElement.type === constants_1.TEXT_TYPE;
+    this._isSelectOneElement = passedElement.type === constants_1.SELECT_ONE_TYPE;
+    this._isSelectMultipleElement = passedElement.type === constants_1.SELECT_MULTIPLE_TYPE;
+    this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement;
+    this.config.searchEnabled = this._isSelectMultipleElement || this.config.searchEnabled;
+    if (!['auto', 'always'].includes("".concat(this.config.renderSelectedChoices))) {
+      this.config.renderSelectedChoices = 'auto';
+    }
+    if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') {
+      var re = userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter);
+      this.config.addItemFilter = re.test.bind(re);
+    }
+    if (this._isTextElement) {
+      this.passedElement = new components_1.WrappedInput({
+        element: passedElement,
+        classNames: this.config.classNames,
+        delimiter: this.config.delimiter
+      });
+    } else {
+      this.passedElement = new components_1.WrappedSelect({
+        element: passedElement,
+        classNames: this.config.classNames,
+        template: function (data) {
+          return _this._templates.option(data);
+        }
+      });
+    }
+    this.initialised = false;
+    this._store = new store_1.default();
+    this._initialState = reducers_1.defaultState;
+    this._currentState = reducers_1.defaultState;
+    this._prevState = reducers_1.defaultState;
+    this._currentValue = '';
+    this._canSearch = !!this.config.searchEnabled;
+    this._isScrollingOnIe = false;
+    this._highlightPosition = 0;
+    this._wasTap = true;
+    this._placeholderValue = this._generatePlaceholderValue();
+    this._baseId = (0, utils_1.generateId)(this.passedElement.element, 'choices-');
+    /**
+     * setting direction in cases where it's explicitly set on passedElement
+     * or when calculated direction is different from the document
+     */
+    this._direction = this.passedElement.dir;
+    if (!this._direction) {
+      var elementDirection = window.getComputedStyle(this.passedElement.element).direction;
+      var documentDirection = window.getComputedStyle(document.documentElement).direction;
+      if (elementDirection !== documentDirection) {
+        this._direction = elementDirection;
+      }
+    }
+    this._idNames = {
+      itemChoice: 'item-choice'
+    };
+    if (this._isSelectElement) {
+      // Assign preset groups from passed element
+      this._presetGroups = this.passedElement.optionGroups;
+      // Assign preset options from passed element
+      this._presetOptions = this.passedElement.options;
+    }
+    // Assign preset choices from passed object
+    this._presetChoices = this.config.choices;
+    // Assign preset items from passed object first
+    this._presetItems = this.config.items;
+    // Add any values passed from attribute
+    if (this.passedElement.value && this._isTextElement) {
+      var splitValues = this.passedElement.value.split(this.config.delimiter);
+      this._presetItems = this._presetItems.concat(splitValues);
+    }
+    // Create array of choices from option elements
+    if (this.passedElement.options) {
+      this.passedElement.options.forEach(function (option) {
+        _this._presetChoices.push({
+          value: option.value,
+          label: option.innerHTML,
+          selected: !!option.selected,
+          disabled: option.disabled || option.parentNode.disabled,
+          placeholder: option.value === '' || option.hasAttribute('placeholder'),
+          customProperties: (0, utils_1.parseCustomProperties)(option.dataset.customProperties)
+        });
+      });
+    }
+    this._render = this._render.bind(this);
+    this._onFocus = this._onFocus.bind(this);
+    this._onBlur = this._onBlur.bind(this);
+    this._onKeyUp = this._onKeyUp.bind(this);
+    this._onKeyDown = this._onKeyDown.bind(this);
+    this._onClick = this._onClick.bind(this);
+    this._onTouchMove = this._onTouchMove.bind(this);
+    this._onTouchEnd = this._onTouchEnd.bind(this);
+    this._onMouseDown = this._onMouseDown.bind(this);
+    this._onMouseOver = this._onMouseOver.bind(this);
+    this._onFormReset = this._onFormReset.bind(this);
+    this._onSelectKey = this._onSelectKey.bind(this);
+    this._onEnterKey = this._onEnterKey.bind(this);
+    this._onEscapeKey = this._onEscapeKey.bind(this);
+    this._onDirectionKey = this._onDirectionKey.bind(this);
+    this._onDeleteKey = this._onDeleteKey.bind(this);
+    // If element has already been initialised with Choices, fail silently
+    if (this.passedElement.isActive) {
+      if (!this.config.silent) {
+        console.warn('Trying to initialise Choices on element already initialised', {
+          element: element
+        });
+      }
+      this.initialised = true;
+      return;
+    }
+    // Let's go
+    this.init();
+  }
+  Object.defineProperty(Choices, "defaults", {
+    get: function () {
+      return Object.preventExtensions({
+        get options() {
+          return USER_DEFAULTS;
+        },
+        get templates() {
+          return templates_1.default;
+        }
+      });
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Choices.prototype.init = function () {
+    if (this.initialised) {
+      return;
+    }
+    this._createTemplates();
+    this._createElements();
+    this._createStructure();
+    this._store.subscribe(this._render);
+    this._render();
+    this._addEventListeners();
+    var shouldDisable = !this.config.addItems || this.passedElement.element.hasAttribute('disabled');
+    if (shouldDisable) {
+      this.disable();
+    }
+    this.initialised = true;
+    var callbackOnInit = this.config.callbackOnInit;
+    // Run callback if it is a function
+    if (callbackOnInit && typeof callbackOnInit === 'function') {
+      callbackOnInit.call(this);
+    }
+  };
+  Choices.prototype.destroy = function () {
+    if (!this.initialised) {
+      return;
+    }
+    this._removeEventListeners();
+    this.passedElement.reveal();
+    this.containerOuter.unwrap(this.passedElement.element);
+    this.clearStore();
+    if (this._isSelectElement) {
+      this.passedElement.options = this._presetOptions;
+    }
+    this._templates = templates_1.default;
+    this.initialised = false;
+  };
+  Choices.prototype.enable = function () {
+    if (this.passedElement.isDisabled) {
+      this.passedElement.enable();
+    }
+    if (this.containerOuter.isDisabled) {
+      this._addEventListeners();
+      this.input.enable();
+      this.containerOuter.enable();
+    }
+    return this;
+  };
+  Choices.prototype.disable = function () {
+    if (!this.passedElement.isDisabled) {
+      this.passedElement.disable();
+    }
+    if (!this.containerOuter.isDisabled) {
+      this._removeEventListeners();
+      this.input.disable();
+      this.containerOuter.disable();
+    }
+    return this;
+  };
+  Choices.prototype.highlightItem = function (item, runEvent) {
+    if (runEvent === void 0) {
+      runEvent = true;
+    }
+    if (!item || !item.id) {
+      return this;
+    }
+    var id = item.id,
+      _a = item.groupId,
+      groupId = _a === void 0 ? -1 : _a,
+      _b = item.value,
+      value = _b === void 0 ? '' : _b,
+      _c = item.label,
+      label = _c === void 0 ? '' : _c;
+    var group = groupId >= 0 ? this._store.getGroupById(groupId) : null;
+    this._store.dispatch((0, items_1.highlightItem)(id, true));
+    if (runEvent) {
+      this.passedElement.triggerEvent(constants_1.EVENTS.highlightItem, {
+        id: id,
+        value: value,
+        label: label,
+        groupValue: group && group.value ? group.value : null
+      });
+    }
+    return this;
+  };
+  Choices.prototype.unhighlightItem = function (item) {
+    if (!item || !item.id) {
+      return this;
+    }
+    var id = item.id,
+      _a = item.groupId,
+      groupId = _a === void 0 ? -1 : _a,
+      _b = item.value,
+      value = _b === void 0 ? '' : _b,
+      _c = item.label,
+      label = _c === void 0 ? '' : _c;
+    var group = groupId >= 0 ? this._store.getGroupById(groupId) : null;
+    this._store.dispatch((0, items_1.highlightItem)(id, false));
+    this.passedElement.triggerEvent(constants_1.EVENTS.highlightItem, {
+      id: id,
+      value: value,
+      label: label,
+      groupValue: group && group.value ? group.value : null
+    });
+    return this;
+  };
+  Choices.prototype.highlightAll = function () {
+    var _this = this;
+    this._store.items.forEach(function (item) {
+      return _this.highlightItem(item);
+    });
+    return this;
+  };
+  Choices.prototype.unhighlightAll = function () {
+    var _this = this;
+    this._store.items.forEach(function (item) {
+      return _this.unhighlightItem(item);
+    });
+    return this;
+  };
+  Choices.prototype.removeActiveItemsByValue = function (value) {
+    var _this = this;
+    this._store.activeItems.filter(function (item) {
+      return item.value === value;
+    }).forEach(function (item) {
+      return _this._removeItem(item);
+    });
+    return this;
+  };
+  Choices.prototype.removeActiveItems = function (excludedId) {
+    var _this = this;
+    this._store.activeItems.filter(function (_a) {
+      var id = _a.id;
+      return id !== excludedId;
+    }).forEach(function (item) {
+      return _this._removeItem(item);
+    });
+    return this;
+  };
+  Choices.prototype.removeHighlightedItems = function (runEvent) {
+    var _this = this;
+    if (runEvent === void 0) {
+      runEvent = false;
+    }
+    this._store.highlightedActiveItems.forEach(function (item) {
+      _this._removeItem(item);
+      // If this action was performed by the user
+      // trigger the event
+      if (runEvent) {
+        _this._triggerChange(item.value);
+      }
+    });
+    return this;
+  };
+  Choices.prototype.showDropdown = function (preventInputFocus) {
+    var _this = this;
+    if (this.dropdown.isActive) {
+      return this;
+    }
+    requestAnimationFrame(function () {
+      _this.dropdown.show();
+      _this.containerOuter.open(_this.dropdown.distanceFromTopWindow);
+      if (!preventInputFocus && _this._canSearch) {
+        _this.input.focus();
+      }
+      _this.passedElement.triggerEvent(constants_1.EVENTS.showDropdown, {});
+    });
+    return this;
+  };
+  Choices.prototype.hideDropdown = function (preventInputBlur) {
+    var _this = this;
+    if (!this.dropdown.isActive) {
+      return this;
+    }
+    requestAnimationFrame(function () {
+      _this.dropdown.hide();
+      _this.containerOuter.close();
+      if (!preventInputBlur && _this._canSearch) {
+        _this.input.removeActiveDescendant();
+        _this.input.blur();
+      }
+      _this.passedElement.triggerEvent(constants_1.EVENTS.hideDropdown, {});
+    });
+    return this;
+  };
+  Choices.prototype.getValue = function (valueOnly) {
+    if (valueOnly === void 0) {
+      valueOnly = false;
+    }
+    var values = this._store.activeItems.reduce(function (selectedItems, item) {
+      var itemValue = valueOnly ? item.value : item;
+      selectedItems.push(itemValue);
+      return selectedItems;
+    }, []);
+    return this._isSelectOneElement ? values[0] : values;
+  };
+  Choices.prototype.setValue = function (items) {
+    var _this = this;
+    if (!this.initialised) {
+      return this;
+    }
+    items.forEach(function (value) {
+      return _this._setChoiceOrItem(value);
+    });
+    return this;
+  };
+  Choices.prototype.setChoiceByValue = function (value) {
+    var _this = this;
+    if (!this.initialised || this._isTextElement) {
+      return this;
+    }
+    // If only one value has been passed, convert to array
+    var choiceValue = Array.isArray(value) ? value : [value];
+    // Loop through each value and
+    choiceValue.forEach(function (val) {
+      return _this._findAndSelectChoiceByValue(val);
+    });
+    return this;
+  };
+  /**
+   * Set choices of select input via an array of objects (or function that returns array of object or promise of it),
+   * a value field name and a label field name.
+   * This behaves the same as passing items via the choices option but can be called after initialising Choices.
+   * This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices.
+   * Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc).
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @example
+   * ```js
+   * const example = new Choices(element);
+   *
+   * example.setChoices([
+   *   {value: 'One', label: 'Label One', disabled: true},
+   *   {value: 'Two', label: 'Label Two', selected: true},
+   *   {value: 'Three', label: 'Label Three'},
+   * ], 'value', 'label', false);
+   * ```
+   *
+   * @example
+   * ```js
+   * const example = new Choices(element);
+   *
+   * example.setChoices(async () => {
+   *   try {
+   *      const items = await fetch('/items');
+   *      return items.json()
+   *   } catch(err) {
+   *      console.error(err)
+   *   }
+   * });
+   * ```
+   *
+   * @example
+   * ```js
+   * const example = new Choices(element);
+   *
+   * example.setChoices([{
+   *   label: 'Group one',
+   *   id: 1,
+   *   disabled: false,
+   *   choices: [
+   *     {value: 'Child One', label: 'Child One', selected: true},
+   *     {value: 'Child Two', label: 'Child Two',  disabled: true},
+   *     {value: 'Child Three', label: 'Child Three'},
+   *   ]
+   * },
+   * {
+   *   label: 'Group two',
+   *   id: 2,
+   *   disabled: false,
+   *   choices: [
+   *     {value: 'Child Four', label: 'Child Four', disabled: true},
+   *     {value: 'Child Five', label: 'Child Five'},
+   *     {value: 'Child Six', label: 'Child Six', customProperties: {
+   *       description: 'Custom description about child six',
+   *       random: 'Another random custom property'
+   *     }},
+   *   ]
+   * }], 'value', 'label', false);
+   * ```
+   */
+  Choices.prototype.setChoices = function (choicesArrayOrFetcher, value, label, replaceChoices) {
+    var _this = this;
+    if (choicesArrayOrFetcher === void 0) {
+      choicesArrayOrFetcher = [];
+    }
+    if (value === void 0) {
+      value = 'value';
+    }
+    if (label === void 0) {
+      label = 'label';
+    }
+    if (replaceChoices === void 0) {
+      replaceChoices = false;
+    }
+    if (!this.initialised) {
+      throw new ReferenceError("setChoices was called on a non-initialized instance of Choices");
+    }
+    if (!this._isSelectElement) {
+      throw new TypeError("setChoices can't be used with INPUT based Choices");
+    }
+    if (typeof value !== 'string' || !value) {
+      throw new TypeError("value parameter must be a name of 'value' field in passed objects");
+    }
+    // Clear choices if needed
+    if (replaceChoices) {
+      this.clearChoices();
+    }
+    if (typeof choicesArrayOrFetcher === 'function') {
+      // it's a choices fetcher function
+      var fetcher_1 = choicesArrayOrFetcher(this);
+      if (typeof Promise === 'function' && fetcher_1 instanceof Promise) {
+        // that's a promise
+        // eslint-disable-next-line no-promise-executor-return
+        return new Promise(function (resolve) {
+          return requestAnimationFrame(resolve);
+        }).then(function () {
+          return _this._handleLoadingState(true);
+        }).then(function () {
+          return fetcher_1;
+        }).then(function (data) {
+          return _this.setChoices(data, value, label, replaceChoices);
+        }).catch(function (err) {
+          if (!_this.config.silent) {
+            console.error(err);
+          }
+        }).then(function () {
+          return _this._handleLoadingState(false);
+        }).then(function () {
+          return _this;
+        });
+      }
+      // function returned something else than promise, let's check if it's an array of choices
+      if (!Array.isArray(fetcher_1)) {
+        throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof fetcher_1));
+      }
+      // recursion with results, it's sync and choices were cleared already
+      return this.setChoices(fetcher_1, value, label, false);
+    }
+    if (!Array.isArray(choicesArrayOrFetcher)) {
+      throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");
+    }
+    this.containerOuter.removeLoadingState();
+    this._startLoading();
+    choicesArrayOrFetcher.forEach(function (groupOrChoice) {
+      if (groupOrChoice.choices) {
+        _this._addGroup({
+          id: groupOrChoice.id ? parseInt("".concat(groupOrChoice.id), 10) : null,
+          group: groupOrChoice,
+          valueKey: value,
+          labelKey: label
+        });
+      } else {
+        var choice = groupOrChoice;
+        _this._addChoice({
+          value: choice[value],
+          label: choice[label],
+          isSelected: !!choice.selected,
+          isDisabled: !!choice.disabled,
+          placeholder: !!choice.placeholder,
+          customProperties: choice.customProperties
+        });
+      }
+    });
+    this._stopLoading();
+    return this;
+  };
+  Choices.prototype.clearChoices = function () {
+    this._store.dispatch((0, choices_1.clearChoices)());
+    return this;
+  };
+  Choices.prototype.clearStore = function () {
+    this._store.dispatch((0, misc_1.clearAll)());
+    return this;
+  };
+  Choices.prototype.clearInput = function () {
+    var shouldSetInputWidth = !this._isSelectOneElement;
+    this.input.clear(shouldSetInputWidth);
+    if (!this._isTextElement && this._canSearch) {
+      this._isSearching = false;
+      this._store.dispatch((0, choices_1.activateChoices)(true));
+    }
+    return this;
+  };
+  Choices.prototype._render = function () {
+    if (this._store.isLoading()) {
+      return;
+    }
+    this._currentState = this._store.state;
+    var stateChanged = this._currentState.choices !== this._prevState.choices || this._currentState.groups !== this._prevState.groups || this._currentState.items !== this._prevState.items;
+    var shouldRenderChoices = this._isSelectElement;
+    var shouldRenderItems = this._currentState.items !== this._prevState.items;
+    if (!stateChanged) {
+      return;
+    }
+    if (shouldRenderChoices) {
+      this._renderChoices();
+    }
+    if (shouldRenderItems) {
+      this._renderItems();
+    }
+    this._prevState = this._currentState;
+  };
+  Choices.prototype._renderChoices = function () {
+    var _this = this;
+    var _a = this._store,
+      activeGroups = _a.activeGroups,
+      activeChoices = _a.activeChoices;
+    var choiceListFragment = document.createDocumentFragment();
+    this.choiceList.clear();
+    if (this.config.resetScrollPosition) {
+      requestAnimationFrame(function () {
+        return _this.choiceList.scrollToTop();
+      });
+    }
+    // If we have grouped options
+    if (activeGroups.length >= 1 && !this._isSearching) {
+      // If we have a placeholder choice along with groups
+      var activePlaceholders = activeChoices.filter(function (activeChoice) {
+        return activeChoice.placeholder === true && activeChoice.groupId === -1;
+      });
+      if (activePlaceholders.length >= 1) {
+        choiceListFragment = this._createChoicesFragment(activePlaceholders, choiceListFragment);
+      }
+      choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment);
+    } else if (activeChoices.length >= 1) {
+      choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment);
+    }
+    // If we have choices to show
+    if (choiceListFragment.childNodes && choiceListFragment.childNodes.length > 0) {
+      var activeItems = this._store.activeItems;
+      var canAddItem = this._canAddItem(activeItems, this.input.value);
+      // ...and we can select them
+      if (canAddItem.response) {
+        // ...append them and highlight the first choice
+        this.choiceList.append(choiceListFragment);
+        this._highlightChoice();
+      } else {
+        var notice = this._getTemplate('notice', canAddItem.notice);
+        this.choiceList.append(notice);
+      }
+    } else {
+      // Otherwise show a notice
+      var dropdownItem = void 0;
+      var notice = void 0;
+      if (this._isSearching) {
+        notice = typeof this.config.noResultsText === 'function' ? this.config.noResultsText() : this.config.noResultsText;
+        dropdownItem = this._getTemplate('notice', notice, 'no-results');
+      } else {
+        notice = typeof this.config.noChoicesText === 'function' ? this.config.noChoicesText() : this.config.noChoicesText;
+        dropdownItem = this._getTemplate('notice', notice, 'no-choices');
+      }
+      this.choiceList.append(dropdownItem);
+    }
+  };
+  Choices.prototype._renderItems = function () {
+    var activeItems = this._store.activeItems || [];
+    this.itemList.clear();
+    // Create a fragment to store our list items
+    // (so we don't have to update the DOM for each item)
+    var itemListFragment = this._createItemsFragment(activeItems);
+    // If we have items to add, append them
+    if (itemListFragment.childNodes) {
+      this.itemList.append(itemListFragment);
+    }
+  };
+  Choices.prototype._createGroupsFragment = function (groups, choices, fragment) {
+    var _this = this;
+    if (fragment === void 0) {
+      fragment = document.createDocumentFragment();
+    }
+    var getGroupChoices = function (group) {
+      return choices.filter(function (choice) {
+        if (_this._isSelectOneElement) {
+          return choice.groupId === group.id;
+        }
+        return choice.groupId === group.id && (_this.config.renderSelectedChoices === 'always' || !choice.selected);
+      });
+    };
+    // If sorting is enabled, filter groups
+    if (this.config.shouldSort) {
+      groups.sort(this.config.sorter);
+    }
+    groups.forEach(function (group) {
+      var groupChoices = getGroupChoices(group);
+      if (groupChoices.length >= 1) {
+        var dropdownGroup = _this._getTemplate('choiceGroup', group);
+        fragment.appendChild(dropdownGroup);
+        _this._createChoicesFragment(groupChoices, fragment, true);
+      }
+    });
+    return fragment;
+  };
+  Choices.prototype._createChoicesFragment = function (choices, fragment, withinGroup) {
+    var _this = this;
+    if (fragment === void 0) {
+      fragment = document.createDocumentFragment();
+    }
+    if (withinGroup === void 0) {
+      withinGroup = false;
+    }
+    // Create a fragment to store our list items (so we don't have to update the DOM for each item)
+    var _a = this.config,
+      renderSelectedChoices = _a.renderSelectedChoices,
+      searchResultLimit = _a.searchResultLimit,
+      renderChoiceLimit = _a.renderChoiceLimit;
+    var filter = this._isSearching ? utils_1.sortByScore : this.config.sorter;
+    var appendChoice = function (choice) {
+      var shouldRender = renderSelectedChoices === 'auto' ? _this._isSelectOneElement || !choice.selected : true;
+      if (shouldRender) {
+        var dropdownItem = _this._getTemplate('choice', choice, _this.config.itemSelectText);
+        fragment.appendChild(dropdownItem);
+      }
+    };
+    var rendererableChoices = choices;
+    if (renderSelectedChoices === 'auto' && !this._isSelectOneElement) {
+      rendererableChoices = choices.filter(function (choice) {
+        return !choice.selected;
+      });
+    }
+    // Split array into placeholders and "normal" choices
+    var _b = rendererableChoices.reduce(function (acc, choice) {
+        if (choice.placeholder) {
+          acc.placeholderChoices.push(choice);
+        } else {
+          acc.normalChoices.push(choice);
+        }
+        return acc;
+      }, {
+        placeholderChoices: [],
+        normalChoices: []
+      }),
+      placeholderChoices = _b.placeholderChoices,
+      normalChoices = _b.normalChoices;
+    // If sorting is enabled or the user is searching, filter choices
+    if (this.config.shouldSort || this._isSearching) {
+      normalChoices.sort(filter);
+    }
+    var choiceLimit = rendererableChoices.length;
+    // Prepend placeholeder
+    var sortedChoices = this._isSelectOneElement ? __spreadArray(__spreadArray([], placeholderChoices, true), normalChoices, true) : normalChoices;
+    if (this._isSearching) {
+      choiceLimit = searchResultLimit;
+    } else if (renderChoiceLimit && renderChoiceLimit > 0 && !withinGroup) {
+      choiceLimit = renderChoiceLimit;
+    }
+    // Add each choice to dropdown within range
+    for (var i = 0; i < choiceLimit; i += 1) {
+      if (sortedChoices[i]) {
+        appendChoice(sortedChoices[i]);
+      }
+    }
+    return fragment;
+  };
+  Choices.prototype._createItemsFragment = function (items, fragment) {
+    var _this = this;
+    if (fragment === void 0) {
+      fragment = document.createDocumentFragment();
+    }
+    // Create fragment to add elements to
+    var _a = this.config,
+      shouldSortItems = _a.shouldSortItems,
+      sorter = _a.sorter,
+      removeItemButton = _a.removeItemButton;
+    // If sorting is enabled, filter items
+    if (shouldSortItems && !this._isSelectOneElement) {
+      items.sort(sorter);
+    }
+    if (this._isTextElement) {
+      // Update the value of the hidden input
+      this.passedElement.value = items.map(function (_a) {
+        var value = _a.value;
+        return value;
+      }).join(this.config.delimiter);
+    } else {
+      // Update the options of the hidden input
+      this.passedElement.options = items;
+    }
+    var addItemToFragment = function (item) {
+      // Create new list element
+      var listItem = _this._getTemplate('item', item, removeItemButton);
+      // Append it to list
+      fragment.appendChild(listItem);
+    };
+    // Add each list item to list
+    items.forEach(addItemToFragment);
+    return fragment;
+  };
+  Choices.prototype._triggerChange = function (value) {
+    if (value === undefined || value === null) {
+      return;
+    }
+    this.passedElement.triggerEvent(constants_1.EVENTS.change, {
+      value: value
+    });
+  };
+  Choices.prototype._selectPlaceholderChoice = function (placeholderChoice) {
+    this._addItem({
+      value: placeholderChoice.value,
+      label: placeholderChoice.label,
+      choiceId: placeholderChoice.id,
+      groupId: placeholderChoice.groupId,
+      placeholder: placeholderChoice.placeholder
+    });
+    this._triggerChange(placeholderChoice.value);
+  };
+  Choices.prototype._handleButtonAction = function (activeItems, element) {
+    if (!activeItems || !element || !this.config.removeItems || !this.config.removeItemButton) {
+      return;
+    }
+    var itemId = element.parentNode && element.parentNode.dataset.id;
+    var itemToRemove = itemId && activeItems.find(function (item) {
+      return item.id === parseInt(itemId, 10);
+    });
+    if (!itemToRemove) {
+      return;
+    }
+    // Remove item associated with button
+    this._removeItem(itemToRemove);
+    this._triggerChange(itemToRemove.value);
+    if (this._isSelectOneElement && this._store.placeholderChoice) {
+      this._selectPlaceholderChoice(this._store.placeholderChoice);
+    }
+  };
+  Choices.prototype._handleItemAction = function (activeItems, element, hasShiftKey) {
+    var _this = this;
+    if (hasShiftKey === void 0) {
+      hasShiftKey = false;
+    }
+    if (!activeItems || !element || !this.config.removeItems || this._isSelectOneElement) {
+      return;
+    }
+    var passedId = element.dataset.id;
+    // We only want to select one item with a click
+    // so we deselect any items that aren't the target
+    // unless shift is being pressed
+    activeItems.forEach(function (item) {
+      if (item.id === parseInt("".concat(passedId), 10) && !item.highlighted) {
+        _this.highlightItem(item);
+      } else if (!hasShiftKey && item.highlighted) {
+        _this.unhighlightItem(item);
+      }
+    });
+    // Focus input as without focus, a user cannot do anything with a
+    // highlighted item
+    this.input.focus();
+  };
+  Choices.prototype._handleChoiceAction = function (activeItems, element) {
+    if (!activeItems || !element) {
+      return;
+    }
+    // If we are clicking on an option
+    var id = element.dataset.id;
+    var choice = id && this._store.getChoiceById(id);
+    if (!choice) {
+      return;
+    }
+    var passedKeyCode = activeItems[0] && activeItems[0].keyCode ? activeItems[0].keyCode : undefined;
+    var hasActiveDropdown = this.dropdown.isActive;
+    // Update choice keyCode
+    choice.keyCode = passedKeyCode;
+    this.passedElement.triggerEvent(constants_1.EVENTS.choice, {
+      choice: choice
+    });
+    if (!choice.selected && !choice.disabled) {
+      var canAddItem = this._canAddItem(activeItems, choice.value);
+      if (canAddItem.response) {
+        this._addItem({
+          value: choice.value,
+          label: choice.label,
+          choiceId: choice.id,
+          groupId: choice.groupId,
+          customProperties: choice.customProperties,
+          placeholder: choice.placeholder,
+          keyCode: choice.keyCode
+        });
+        this._triggerChange(choice.value);
+      }
+    }
+    this.clearInput();
+    // We want to close the dropdown if we are dealing with a single select box
+    if (hasActiveDropdown && this._isSelectOneElement) {
+      this.hideDropdown(true);
+      this.containerOuter.focus();
+    }
+  };
+  Choices.prototype._handleBackspace = function (activeItems) {
+    if (!this.config.removeItems || !activeItems) {
+      return;
+    }
+    var lastItem = activeItems[activeItems.length - 1];
+    var hasHighlightedItems = activeItems.some(function (item) {
+      return item.highlighted;
+    });
+    // If editing the last item is allowed and there are not other selected items,
+    // we can edit the item value. Otherwise if we can remove items, remove all selected items
+    if (this.config.editItems && !hasHighlightedItems && lastItem) {
+      this.input.value = lastItem.value;
+      this.input.setWidth();
+      this._removeItem(lastItem);
+      this._triggerChange(lastItem.value);
+    } else {
+      if (!hasHighlightedItems) {
+        // Highlight last item if none already highlighted
+        this.highlightItem(lastItem, false);
+      }
+      this.removeHighlightedItems(true);
+    }
+  };
+  Choices.prototype._startLoading = function () {
+    this._store.dispatch((0, misc_1.setIsLoading)(true));
+  };
+  Choices.prototype._stopLoading = function () {
+    this._store.dispatch((0, misc_1.setIsLoading)(false));
+  };
+  Choices.prototype._handleLoadingState = function (setLoading) {
+    if (setLoading === void 0) {
+      setLoading = true;
+    }
+    var placeholderItem = this.itemList.getChild(".".concat(this.config.classNames.placeholder));
+    if (setLoading) {
+      this.disable();
+      this.containerOuter.addLoadingState();
+      if (this._isSelectOneElement) {
+        if (!placeholderItem) {
+          placeholderItem = this._getTemplate('placeholder', this.config.loadingText);
+          if (placeholderItem) {
+            this.itemList.append(placeholderItem);
+          }
+        } else {
+          placeholderItem.innerHTML = this.config.loadingText;
+        }
+      } else {
+        this.input.placeholder = this.config.loadingText;
+      }
+    } else {
+      this.enable();
+      this.containerOuter.removeLoadingState();
+      if (this._isSelectOneElement) {
+        if (placeholderItem) {
+          placeholderItem.innerHTML = this._placeholderValue || '';
+        }
+      } else {
+        this.input.placeholder = this._placeholderValue || '';
+      }
+    }
+  };
+  Choices.prototype._handleSearch = function (value) {
+    if (!this.input.isFocussed) {
+      return;
+    }
+    var choices = this._store.choices;
+    var _a = this.config,
+      searchFloor = _a.searchFloor,
+      searchChoices = _a.searchChoices;
+    var hasUnactiveChoices = choices.some(function (option) {
+      return !option.active;
+    });
+    // Check that we have a value to search and the input was an alphanumeric character
+    if (value !== null && typeof value !== 'undefined' && value.length >= searchFloor) {
+      var resultCount = searchChoices ? this._searchChoices(value) : 0;
+      // Trigger search event
+      this.passedElement.triggerEvent(constants_1.EVENTS.search, {
+        value: value,
+        resultCount: resultCount
+      });
+    } else if (hasUnactiveChoices) {
+      // Otherwise reset choices to active
+      this._isSearching = false;
+      this._store.dispatch((0, choices_1.activateChoices)(true));
+    }
+  };
+  Choices.prototype._canAddItem = function (activeItems, value) {
+    var canAddItem = true;
+    var notice = typeof this.config.addItemText === 'function' ? this.config.addItemText(value) : this.config.addItemText;
+    if (!this._isSelectOneElement) {
+      var isDuplicateValue = (0, utils_1.existsInArray)(activeItems, value);
+      if (this.config.maxItemCount > 0 && this.config.maxItemCount <= activeItems.length) {
+        // If there is a max entry limit and we have reached that limit
+        // don't update
+        canAddItem = false;
+        notice = typeof this.config.maxItemText === 'function' ? this.config.maxItemText(this.config.maxItemCount) : this.config.maxItemText;
+      }
+      if (!this.config.duplicateItemsAllowed && isDuplicateValue && canAddItem) {
+        canAddItem = false;
+        notice = typeof this.config.uniqueItemText === 'function' ? this.config.uniqueItemText(value) : this.config.uniqueItemText;
+      }
+      if (this._isTextElement && this.config.addItems && canAddItem && typeof this.config.addItemFilter === 'function' && !this.config.addItemFilter(value)) {
+        canAddItem = false;
+        notice = typeof this.config.customAddItemText === 'function' ? this.config.customAddItemText(value) : this.config.customAddItemText;
+      }
+    }
+    return {
+      response: canAddItem,
+      notice: notice
+    };
+  };
+  Choices.prototype._searchChoices = function (value) {
+    var newValue = typeof value === 'string' ? value.trim() : value;
+    var currentValue = typeof this._currentValue === 'string' ? this._currentValue.trim() : this._currentValue;
+    if (newValue.length < 1 && newValue === "".concat(currentValue, " ")) {
+      return 0;
+    }
+    // If new value matches the desired length and is not the same as the current value with a space
+    var haystack = this._store.searchableChoices;
+    var needle = newValue;
+    var options = Object.assign(this.config.fuseOptions, {
+      keys: __spreadArray([], this.config.searchFields, true),
+      includeMatches: true
+    });
+    var fuse = new fuse_js_1.default(haystack, options);
+    var results = fuse.search(needle); // see https://github.com/krisk/Fuse/issues/303
+    this._currentValue = newValue;
+    this._highlightPosition = 0;
+    this._isSearching = true;
+    this._store.dispatch((0, choices_1.filterChoices)(results));
+    return results.length;
+  };
+  Choices.prototype._addEventListeners = function () {
+    var documentElement = document.documentElement;
+    // capture events - can cancel event processing or propagation
+    documentElement.addEventListener('touchend', this._onTouchEnd, true);
+    this.containerOuter.element.addEventListener('keydown', this._onKeyDown, true);
+    this.containerOuter.element.addEventListener('mousedown', this._onMouseDown, true);
+    // passive events - doesn't call `preventDefault` or `stopPropagation`
+    documentElement.addEventListener('click', this._onClick, {
+      passive: true
+    });
+    documentElement.addEventListener('touchmove', this._onTouchMove, {
+      passive: true
+    });
+    this.dropdown.element.addEventListener('mouseover', this._onMouseOver, {
+      passive: true
+    });
+    if (this._isSelectOneElement) {
+      this.containerOuter.element.addEventListener('focus', this._onFocus, {
+        passive: true
+      });
+      this.containerOuter.element.addEventListener('blur', this._onBlur, {
+        passive: true
+      });
+    }
+    this.input.element.addEventListener('keyup', this._onKeyUp, {
+      passive: true
+    });
+    this.input.element.addEventListener('focus', this._onFocus, {
+      passive: true
+    });
+    this.input.element.addEventListener('blur', this._onBlur, {
+      passive: true
+    });
+    if (this.input.element.form) {
+      this.input.element.form.addEventListener('reset', this._onFormReset, {
+        passive: true
+      });
+    }
+    this.input.addEventListeners();
+  };
+  Choices.prototype._removeEventListeners = function () {
+    var documentElement = document.documentElement;
+    documentElement.removeEventListener('touchend', this._onTouchEnd, true);
+    this.containerOuter.element.removeEventListener('keydown', this._onKeyDown, true);
+    this.containerOuter.element.removeEventListener('mousedown', this._onMouseDown, true);
+    documentElement.removeEventListener('click', this._onClick);
+    documentElement.removeEventListener('touchmove', this._onTouchMove);
+    this.dropdown.element.removeEventListener('mouseover', this._onMouseOver);
+    if (this._isSelectOneElement) {
+      this.containerOuter.element.removeEventListener('focus', this._onFocus);
+      this.containerOuter.element.removeEventListener('blur', this._onBlur);
+    }
+    this.input.element.removeEventListener('keyup', this._onKeyUp);
+    this.input.element.removeEventListener('focus', this._onFocus);
+    this.input.element.removeEventListener('blur', this._onBlur);
+    if (this.input.element.form) {
+      this.input.element.form.removeEventListener('reset', this._onFormReset);
+    }
+    this.input.removeEventListeners();
+  };
+  Choices.prototype._onKeyDown = function (event) {
+    var keyCode = event.keyCode;
+    var activeItems = this._store.activeItems;
+    var hasFocusedInput = this.input.isFocussed;
+    var hasActiveDropdown = this.dropdown.isActive;
+    var hasItems = this.itemList.hasChildren();
+    var keyString = String.fromCharCode(keyCode);
+    // eslint-disable-next-line no-control-regex
+    var wasPrintableChar = /[^\x00-\x1F]/.test(keyString);
+    var BACK_KEY = constants_1.KEY_CODES.BACK_KEY,
+      DELETE_KEY = constants_1.KEY_CODES.DELETE_KEY,
+      ENTER_KEY = constants_1.KEY_CODES.ENTER_KEY,
+      A_KEY = constants_1.KEY_CODES.A_KEY,
+      ESC_KEY = constants_1.KEY_CODES.ESC_KEY,
+      UP_KEY = constants_1.KEY_CODES.UP_KEY,
+      DOWN_KEY = constants_1.KEY_CODES.DOWN_KEY,
+      PAGE_UP_KEY = constants_1.KEY_CODES.PAGE_UP_KEY,
+      PAGE_DOWN_KEY = constants_1.KEY_CODES.PAGE_DOWN_KEY;
+    if (!this._isTextElement && !hasActiveDropdown && wasPrintableChar) {
+      this.showDropdown();
+      if (!this.input.isFocussed) {
+        /*
+          We update the input value with the pressed key as
+          the input was not focussed at the time of key press
+          therefore does not have the value of the key.
+        */
+        this.input.value += event.key.toLowerCase();
+      }
+    }
+    switch (keyCode) {
+      case A_KEY:
+        return this._onSelectKey(event, hasItems);
+      case ENTER_KEY:
+        return this._onEnterKey(event, activeItems, hasActiveDropdown);
+      case ESC_KEY:
+        return this._onEscapeKey(hasActiveDropdown);
+      case UP_KEY:
+      case PAGE_UP_KEY:
+      case DOWN_KEY:
+      case PAGE_DOWN_KEY:
+        return this._onDirectionKey(event, hasActiveDropdown);
+      case DELETE_KEY:
+      case BACK_KEY:
+        return this._onDeleteKey(event, activeItems, hasFocusedInput);
+      default:
+    }
+  };
+  Choices.prototype._onKeyUp = function (_a) {
+    var target = _a.target,
+      keyCode = _a.keyCode;
+    var value = this.input.value;
+    var activeItems = this._store.activeItems;
+    var canAddItem = this._canAddItem(activeItems, value);
+    var backKey = constants_1.KEY_CODES.BACK_KEY,
+      deleteKey = constants_1.KEY_CODES.DELETE_KEY;
+    // We are typing into a text input and have a value, we want to show a dropdown
+    // notice. Otherwise hide the dropdown
+    if (this._isTextElement) {
+      var canShowDropdownNotice = canAddItem.notice && value;
+      if (canShowDropdownNotice) {
+        var dropdownItem = this._getTemplate('notice', canAddItem.notice);
+        this.dropdown.element.innerHTML = dropdownItem.outerHTML;
+        this.showDropdown(true);
+      } else {
+        this.hideDropdown(true);
+      }
+    } else {
+      var wasRemovalKeyCode = keyCode === backKey || keyCode === deleteKey;
+      var userHasRemovedValue = wasRemovalKeyCode && target && !target.value;
+      var canReactivateChoices = !this._isTextElement && this._isSearching;
+      var canSearch = this._canSearch && canAddItem.response;
+      if (userHasRemovedValue && canReactivateChoices) {
+        this._isSearching = false;
+        this._store.dispatch((0, choices_1.activateChoices)(true));
+      } else if (canSearch) {
+        this._handleSearch(this.input.rawValue);
+      }
+    }
+    this._canSearch = this.config.searchEnabled;
+  };
+  Choices.prototype._onSelectKey = function (event, hasItems) {
+    var ctrlKey = event.ctrlKey,
+      metaKey = event.metaKey;
+    var hasCtrlDownKeyPressed = ctrlKey || metaKey;
+    // If CTRL + A or CMD + A have been pressed and there are items to select
+    if (hasCtrlDownKeyPressed && hasItems) {
+      this._canSearch = false;
+      var shouldHightlightAll = this.config.removeItems && !this.input.value && this.input.element === document.activeElement;
+      if (shouldHightlightAll) {
+        this.highlightAll();
+      }
+    }
+  };
+  Choices.prototype._onEnterKey = function (event, activeItems, hasActiveDropdown) {
+    var target = event.target;
+    var enterKey = constants_1.KEY_CODES.ENTER_KEY;
+    var targetWasButton = target && target.hasAttribute('data-button');
+    if (this._isTextElement && target && target.value) {
+      var value = this.input.value;
+      var canAddItem = this._canAddItem(activeItems, value);
+      if (canAddItem.response) {
+        this.hideDropdown(true);
+        this._addItem({
+          value: value
+        });
+        this._triggerChange(value);
+        this.clearInput();
+      }
+    }
+    if (targetWasButton) {
+      this._handleButtonAction(activeItems, target);
+      event.preventDefault();
+    }
+    if (hasActiveDropdown) {
+      var highlightedChoice = this.dropdown.getChild(".".concat(this.config.classNames.highlightedState));
+      if (highlightedChoice) {
+        // add enter keyCode value
+        if (activeItems[0]) {
+          activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign
+        }
+
+        this._handleChoiceAction(activeItems, highlightedChoice);
+      }
+      event.preventDefault();
+    } else if (this._isSelectOneElement) {
+      this.showDropdown();
+      event.preventDefault();
+    }
+  };
+  Choices.prototype._onEscapeKey = function (hasActiveDropdown) {
+    if (hasActiveDropdown) {
+      this.hideDropdown(true);
+      this.containerOuter.focus();
+    }
+  };
+  Choices.prototype._onDirectionKey = function (event, hasActiveDropdown) {
+    var keyCode = event.keyCode,
+      metaKey = event.metaKey;
+    var downKey = constants_1.KEY_CODES.DOWN_KEY,
+      pageUpKey = constants_1.KEY_CODES.PAGE_UP_KEY,
+      pageDownKey = constants_1.KEY_CODES.PAGE_DOWN_KEY;
+    // If up or down key is pressed, traverse through options
+    if (hasActiveDropdown || this._isSelectOneElement) {
+      this.showDropdown();
+      this._canSearch = false;
+      var directionInt = keyCode === downKey || keyCode === pageDownKey ? 1 : -1;
+      var skipKey = metaKey || keyCode === pageDownKey || keyCode === pageUpKey;
+      var selectableChoiceIdentifier = '[data-choice-selectable]';
+      var nextEl = void 0;
+      if (skipKey) {
+        if (directionInt > 0) {
+          nextEl = this.dropdown.element.querySelector("".concat(selectableChoiceIdentifier, ":last-of-type"));
+        } else {
+          nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier);
+        }
+      } else {
+        var currentEl = this.dropdown.element.querySelector(".".concat(this.config.classNames.highlightedState));
+        if (currentEl) {
+          nextEl = (0, utils_1.getAdjacentEl)(currentEl, selectableChoiceIdentifier, directionInt);
+        } else {
+          nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier);
+        }
+      }
+      if (nextEl) {
+        // We prevent default to stop the cursor moving
+        // when pressing the arrow
+        if (!(0, utils_1.isScrolledIntoView)(nextEl, this.choiceList.element, directionInt)) {
+          this.choiceList.scrollToChildElement(nextEl, directionInt);
+        }
+        this._highlightChoice(nextEl);
+      }
+      // Prevent default to maintain cursor position whilst
+      // traversing dropdown options
+      event.preventDefault();
+    }
+  };
+  Choices.prototype._onDeleteKey = function (event, activeItems, hasFocusedInput) {
+    var target = event.target;
+    // If backspace or delete key is pressed and the input has no value
+    if (!this._isSelectOneElement && !target.value && hasFocusedInput) {
+      this._handleBackspace(activeItems);
+      event.preventDefault();
+    }
+  };
+  Choices.prototype._onTouchMove = function () {
+    if (this._wasTap) {
+      this._wasTap = false;
+    }
+  };
+  Choices.prototype._onTouchEnd = function (event) {
+    var target = (event || event.touches[0]).target;
+    var touchWasWithinContainer = this._wasTap && this.containerOuter.element.contains(target);
+    if (touchWasWithinContainer) {
+      var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element;
+      if (containerWasExactTarget) {
+        if (this._isTextElement) {
+          this.input.focus();
+        } else if (this._isSelectMultipleElement) {
+          this.showDropdown();
+        }
+      }
+      // Prevents focus event firing
+      event.stopPropagation();
+    }
+    this._wasTap = true;
+  };
+  /**
+   * Handles mousedown event in capture mode for containetOuter.element
+   */
+  Choices.prototype._onMouseDown = function (event) {
+    var target = event.target;
+    if (!(target instanceof HTMLElement)) {
+      return;
+    }
+    // If we have our mouse down on the scrollbar and are on IE11...
+    if (IS_IE11 && this.choiceList.element.contains(target)) {
+      // check if click was on a scrollbar area
+      var firstChoice = this.choiceList.element.firstElementChild;
+      var isOnScrollbar = this._direction === 'ltr' ? event.offsetX >= firstChoice.offsetWidth : event.offsetX < firstChoice.offsetLeft;
+      this._isScrollingOnIe = isOnScrollbar;
+    }
+    if (target === this.input.element) {
+      return;
+    }
+    var item = target.closest('[data-button],[data-item],[data-choice]');
+    if (item instanceof HTMLElement) {
+      var hasShiftKey = event.shiftKey;
+      var activeItems = this._store.activeItems;
+      var dataset = item.dataset;
+      if ('button' in dataset) {
+        this._handleButtonAction(activeItems, item);
+      } else if ('item' in dataset) {
+        this._handleItemAction(activeItems, item, hasShiftKey);
+      } else if ('choice' in dataset) {
+        this._handleChoiceAction(activeItems, item);
+      }
+    }
+    event.preventDefault();
+  };
+  /**
+   * Handles mouseover event over this.dropdown
+   * @param {MouseEvent} event
+   */
+  Choices.prototype._onMouseOver = function (_a) {
+    var target = _a.target;
+    if (target instanceof HTMLElement && 'choice' in target.dataset) {
+      this._highlightChoice(target);
+    }
+  };
+  Choices.prototype._onClick = function (_a) {
+    var target = _a.target;
+    var clickWasWithinContainer = this.containerOuter.element.contains(target);
+    if (clickWasWithinContainer) {
+      if (!this.dropdown.isActive && !this.containerOuter.isDisabled) {
+        if (this._isTextElement) {
+          if (document.activeElement !== this.input.element) {
+            this.input.focus();
+          }
+        } else {
+          this.showDropdown();
+          this.containerOuter.focus();
+        }
+      } else if (this._isSelectOneElement && target !== this.input.element && !this.dropdown.element.contains(target)) {
+        this.hideDropdown();
+      }
+    } else {
+      var hasHighlightedItems = this._store.highlightedActiveItems.length > 0;
+      if (hasHighlightedItems) {
+        this.unhighlightAll();
+      }
+      this.containerOuter.removeFocusState();
+      this.hideDropdown(true);
+    }
+  };
+  Choices.prototype._onFocus = function (_a) {
+    var _b;
+    var _this = this;
+    var target = _a.target;
+    var focusWasWithinContainer = target && this.containerOuter.element.contains(target);
+    if (!focusWasWithinContainer) {
+      return;
+    }
+    var focusActions = (_b = {}, _b[constants_1.TEXT_TYPE] = function () {
+      if (target === _this.input.element) {
+        _this.containerOuter.addFocusState();
+      }
+    }, _b[constants_1.SELECT_ONE_TYPE] = function () {
+      _this.containerOuter.addFocusState();
+      if (target === _this.input.element) {
+        _this.showDropdown(true);
+      }
+    }, _b[constants_1.SELECT_MULTIPLE_TYPE] = function () {
+      if (target === _this.input.element) {
+        _this.showDropdown(true);
+        // If element is a select box, the focused element is the container and the dropdown
+        // isn't already open, focus and show dropdown
+        _this.containerOuter.addFocusState();
+      }
+    }, _b);
+    focusActions[this.passedElement.element.type]();
+  };
+  Choices.prototype._onBlur = function (_a) {
+    var _b;
+    var _this = this;
+    var target = _a.target;
+    var blurWasWithinContainer = target && this.containerOuter.element.contains(target);
+    if (blurWasWithinContainer && !this._isScrollingOnIe) {
+      var activeItems = this._store.activeItems;
+      var hasHighlightedItems_1 = activeItems.some(function (item) {
+        return item.highlighted;
+      });
+      var blurActions = (_b = {}, _b[constants_1.TEXT_TYPE] = function () {
+        if (target === _this.input.element) {
+          _this.containerOuter.removeFocusState();
+          if (hasHighlightedItems_1) {
+            _this.unhighlightAll();
+          }
+          _this.hideDropdown(true);
+        }
+      }, _b[constants_1.SELECT_ONE_TYPE] = function () {
+        _this.containerOuter.removeFocusState();
+        if (target === _this.input.element || target === _this.containerOuter.element && !_this._canSearch) {
+          _this.hideDropdown(true);
+        }
+      }, _b[constants_1.SELECT_MULTIPLE_TYPE] = function () {
+        if (target === _this.input.element) {
+          _this.containerOuter.removeFocusState();
+          _this.hideDropdown(true);
+          if (hasHighlightedItems_1) {
+            _this.unhighlightAll();
+          }
+        }
+      }, _b);
+      blurActions[this.passedElement.element.type]();
+    } else {
+      // On IE11, clicking the scollbar blurs our input and thus
+      // closes the dropdown. To stop this, we refocus our input
+      // if we know we are on IE *and* are scrolling.
+      this._isScrollingOnIe = false;
+      this.input.element.focus();
+    }
+  };
+  Choices.prototype._onFormReset = function () {
+    this._store.dispatch((0, misc_1.resetTo)(this._initialState));
+  };
+  Choices.prototype._highlightChoice = function (el) {
+    var _this = this;
+    if (el === void 0) {
+      el = null;
+    }
+    var choices = Array.from(this.dropdown.element.querySelectorAll('[data-choice-selectable]'));
+    if (!choices.length) {
+      return;
+    }
+    var passedEl = el;
+    var highlightedChoices = Array.from(this.dropdown.element.querySelectorAll(".".concat(this.config.classNames.highlightedState)));
+    // Remove any highlighted choices
+    highlightedChoices.forEach(function (choice) {
+      choice.classList.remove(_this.config.classNames.highlightedState);
+      choice.setAttribute('aria-selected', 'false');
+    });
+    if (passedEl) {
+      this._highlightPosition = choices.indexOf(passedEl);
+    } else {
+      // Highlight choice based on last known highlight location
+      if (choices.length > this._highlightPosition) {
+        // If we have an option to highlight
+        passedEl = choices[this._highlightPosition];
+      } else {
+        // Otherwise highlight the option before
+        passedEl = choices[choices.length - 1];
+      }
+      if (!passedEl) {
+        passedEl = choices[0];
+      }
+    }
+    passedEl.classList.add(this.config.classNames.highlightedState);
+    passedEl.setAttribute('aria-selected', 'true');
+    this.passedElement.triggerEvent(constants_1.EVENTS.highlightChoice, {
+      el: passedEl
+    });
+    if (this.dropdown.isActive) {
+      // IE11 ignores aria-label and blocks virtual keyboard
+      // if aria-activedescendant is set without a dropdown
+      this.input.setActiveDescendant(passedEl.id);
+      this.containerOuter.setActiveDescendant(passedEl.id);
+    }
+  };
+  Choices.prototype._addItem = function (_a) {
+    var value = _a.value,
+      _b = _a.label,
+      label = _b === void 0 ? null : _b,
+      _c = _a.choiceId,
+      choiceId = _c === void 0 ? -1 : _c,
+      _d = _a.groupId,
+      groupId = _d === void 0 ? -1 : _d,
+      _e = _a.customProperties,
+      customProperties = _e === void 0 ? {} : _e,
+      _f = _a.placeholder,
+      placeholder = _f === void 0 ? false : _f,
+      _g = _a.keyCode,
+      keyCode = _g === void 0 ? -1 : _g;
+    var passedValue = typeof value === 'string' ? value.trim() : value;
+    var items = this._store.items;
+    var passedLabel = label || passedValue;
+    var passedOptionId = choiceId || -1;
+    var group = groupId >= 0 ? this._store.getGroupById(groupId) : null;
+    var id = items ? items.length + 1 : 1;
+    // If a prepended value has been passed, prepend it
+    if (this.config.prependValue) {
+      passedValue = this.config.prependValue + passedValue.toString();
+    }
+    // If an appended value has been passed, append it
+    if (this.config.appendValue) {
+      passedValue += this.config.appendValue.toString();
+    }
+    this._store.dispatch((0, items_1.addItem)({
+      value: passedValue,
+      label: passedLabel,
+      id: id,
+      choiceId: passedOptionId,
+      groupId: groupId,
+      customProperties: customProperties,
+      placeholder: placeholder,
+      keyCode: keyCode
+    }));
+    if (this._isSelectOneElement) {
+      this.removeActiveItems(id);
+    }
+    // Trigger change event
+    this.passedElement.triggerEvent(constants_1.EVENTS.addItem, {
+      id: id,
+      value: passedValue,
+      label: passedLabel,
+      customProperties: customProperties,
+      groupValue: group && group.value ? group.value : null,
+      keyCode: keyCode
+    });
+  };
+  Choices.prototype._removeItem = function (item) {
+    var id = item.id,
+      value = item.value,
+      label = item.label,
+      customProperties = item.customProperties,
+      choiceId = item.choiceId,
+      groupId = item.groupId;
+    var group = groupId && groupId >= 0 ? this._store.getGroupById(groupId) : null;
+    if (!id || !choiceId) {
+      return;
+    }
+    this._store.dispatch((0, items_1.removeItem)(id, choiceId));
+    this.passedElement.triggerEvent(constants_1.EVENTS.removeItem, {
+      id: id,
+      value: value,
+      label: label,
+      customProperties: customProperties,
+      groupValue: group && group.value ? group.value : null
+    });
+  };
+  Choices.prototype._addChoice = function (_a) {
+    var value = _a.value,
+      _b = _a.label,
+      label = _b === void 0 ? null : _b,
+      _c = _a.isSelected,
+      isSelected = _c === void 0 ? false : _c,
+      _d = _a.isDisabled,
+      isDisabled = _d === void 0 ? false : _d,
+      _e = _a.groupId,
+      groupId = _e === void 0 ? -1 : _e,
+      _f = _a.customProperties,
+      customProperties = _f === void 0 ? {} : _f,
+      _g = _a.placeholder,
+      placeholder = _g === void 0 ? false : _g,
+      _h = _a.keyCode,
+      keyCode = _h === void 0 ? -1 : _h;
+    if (typeof value === 'undefined' || value === null) {
+      return;
+    }
+    // Generate unique id
+    var choices = this._store.choices;
+    var choiceLabel = label || value;
+    var choiceId = choices ? choices.length + 1 : 1;
+    var choiceElementId = "".concat(this._baseId, "-").concat(this._idNames.itemChoice, "-").concat(choiceId);
+    this._store.dispatch((0, choices_1.addChoice)({
+      id: choiceId,
+      groupId: groupId,
+      elementId: choiceElementId,
+      value: value,
+      label: choiceLabel,
+      disabled: isDisabled,
+      customProperties: customProperties,
+      placeholder: placeholder,
+      keyCode: keyCode
+    }));
+    if (isSelected) {
+      this._addItem({
+        value: value,
+        label: choiceLabel,
+        choiceId: choiceId,
+        customProperties: customProperties,
+        placeholder: placeholder,
+        keyCode: keyCode
+      });
+    }
+  };
+  Choices.prototype._addGroup = function (_a) {
+    var _this = this;
+    var group = _a.group,
+      id = _a.id,
+      _b = _a.valueKey,
+      valueKey = _b === void 0 ? 'value' : _b,
+      _c = _a.labelKey,
+      labelKey = _c === void 0 ? 'label' : _c;
+    var groupChoices = (0, utils_1.isType)('Object', group) ? group.choices : Array.from(group.getElementsByTagName('OPTION'));
+    var groupId = id || Math.floor(new Date().valueOf() * Math.random());
+    var isDisabled = group.disabled ? group.disabled : false;
+    if (groupChoices) {
+      this._store.dispatch((0, groups_1.addGroup)({
+        value: group.label,
+        id: groupId,
+        active: true,
+        disabled: isDisabled
+      }));
+      var addGroupChoices = function (choice) {
+        var isOptDisabled = choice.disabled || choice.parentNode && choice.parentNode.disabled;
+        _this._addChoice({
+          value: choice[valueKey],
+          label: (0, utils_1.isType)('Object', choice) ? choice[labelKey] : choice.innerHTML,
+          isSelected: choice.selected,
+          isDisabled: isOptDisabled,
+          groupId: groupId,
+          customProperties: choice.customProperties,
+          placeholder: choice.placeholder
+        });
+      };
+      groupChoices.forEach(addGroupChoices);
+    } else {
+      this._store.dispatch((0, groups_1.addGroup)({
+        value: group.label,
+        id: group.id,
+        active: false,
+        disabled: group.disabled
+      }));
+    }
+  };
+  Choices.prototype._getTemplate = function (template) {
+    var _a;
+    var args = [];
+    for (var _i = 1; _i < arguments.length; _i++) {
+      args[_i - 1] = arguments[_i];
+    }
+    return (_a = this._templates[template]).call.apply(_a, __spreadArray([this, this.config], args, false));
+  };
+  Choices.prototype._createTemplates = function () {
+    var callbackOnCreateTemplates = this.config.callbackOnCreateTemplates;
+    var userTemplates = {};
+    if (callbackOnCreateTemplates && typeof callbackOnCreateTemplates === 'function') {
+      userTemplates = callbackOnCreateTemplates.call(this, utils_1.strToEl);
+    }
+    this._templates = (0, deepmerge_1.default)(templates_1.default, userTemplates);
+  };
+  Choices.prototype._createElements = function () {
+    this.containerOuter = new components_1.Container({
+      element: this._getTemplate('containerOuter', this._direction, this._isSelectElement, this._isSelectOneElement, this.config.searchEnabled, this.passedElement.element.type, this.config.labelId),
+      classNames: this.config.classNames,
+      type: this.passedElement.element.type,
+      position: this.config.position
+    });
+    this.containerInner = new components_1.Container({
+      element: this._getTemplate('containerInner'),
+      classNames: this.config.classNames,
+      type: this.passedElement.element.type,
+      position: this.config.position
+    });
+    this.input = new components_1.Input({
+      element: this._getTemplate('input', this._placeholderValue),
+      classNames: this.config.classNames,
+      type: this.passedElement.element.type,
+      preventPaste: !this.config.paste
+    });
+    this.choiceList = new components_1.List({
+      element: this._getTemplate('choiceList', this._isSelectOneElement)
+    });
+    this.itemList = new components_1.List({
+      element: this._getTemplate('itemList', this._isSelectOneElement)
+    });
+    this.dropdown = new components_1.Dropdown({
+      element: this._getTemplate('dropdown'),
+      classNames: this.config.classNames,
+      type: this.passedElement.element.type
+    });
+  };
+  Choices.prototype._createStructure = function () {
+    // Hide original element
+    this.passedElement.conceal();
+    // Wrap input in container preserving DOM ordering
+    this.containerInner.wrap(this.passedElement.element);
+    // Wrapper inner container with outer container
+    this.containerOuter.wrap(this.containerInner.element);
+    if (this._isSelectOneElement) {
+      this.input.placeholder = this.config.searchPlaceholderValue || '';
+    } else if (this._placeholderValue) {
+      this.input.placeholder = this._placeholderValue;
+      this.input.setWidth();
+    }
+    this.containerOuter.element.appendChild(this.containerInner.element);
+    this.containerOuter.element.appendChild(this.dropdown.element);
+    this.containerInner.element.appendChild(this.itemList.element);
+    if (!this._isTextElement) {
+      this.dropdown.element.appendChild(this.choiceList.element);
+    }
+    if (!this._isSelectOneElement) {
+      this.containerInner.element.appendChild(this.input.element);
+    } else if (this.config.searchEnabled) {
+      this.dropdown.element.insertBefore(this.input.element, this.dropdown.element.firstChild);
+    }
+    if (this._isSelectElement) {
+      this._highlightPosition = 0;
+      this._isSearching = false;
+      this._startLoading();
+      if (this._presetGroups.length) {
+        this._addPredefinedGroups(this._presetGroups);
+      } else {
+        this._addPredefinedChoices(this._presetChoices);
+      }
+      this._stopLoading();
+    }
+    if (this._isTextElement) {
+      this._addPredefinedItems(this._presetItems);
+    }
+  };
+  Choices.prototype._addPredefinedGroups = function (groups) {
+    var _this = this;
+    // If we have a placeholder option
+    var placeholderChoice = this.passedElement.placeholderOption;
+    if (placeholderChoice && placeholderChoice.parentNode && placeholderChoice.parentNode.tagName === 'SELECT') {
+      this._addChoice({
+        value: placeholderChoice.value,
+        label: placeholderChoice.innerHTML,
+        isSelected: placeholderChoice.selected,
+        isDisabled: placeholderChoice.disabled,
+        placeholder: true
+      });
+    }
+    groups.forEach(function (group) {
+      return _this._addGroup({
+        group: group,
+        id: group.id || null
+      });
+    });
+  };
+  Choices.prototype._addPredefinedChoices = function (choices) {
+    var _this = this;
+    // If sorting is enabled or the user is searching, filter choices
+    if (this.config.shouldSort) {
+      choices.sort(this.config.sorter);
+    }
+    var hasSelectedChoice = choices.some(function (choice) {
+      return choice.selected;
+    });
+    var firstEnabledChoiceIndex = choices.findIndex(function (choice) {
+      return choice.disabled === undefined || !choice.disabled;
+    });
+    choices.forEach(function (choice, index) {
+      var _a = choice.value,
+        value = _a === void 0 ? '' : _a,
+        label = choice.label,
+        customProperties = choice.customProperties,
+        placeholder = choice.placeholder;
+      if (_this._isSelectElement) {
+        // If the choice is actually a group
+        if (choice.choices) {
+          _this._addGroup({
+            group: choice,
+            id: choice.id || null
+          });
+        } else {
+          /**
+           * If there is a selected choice already or the choice is not the first in
+           * the array, add each choice normally.
+           *
+           * Otherwise we pre-select the first enabled choice in the array ("select-one" only)
+           */
+          var shouldPreselect = _this._isSelectOneElement && !hasSelectedChoice && index === firstEnabledChoiceIndex;
+          var isSelected = shouldPreselect ? true : choice.selected;
+          var isDisabled = choice.disabled;
+          _this._addChoice({
+            value: value,
+            label: label,
+            isSelected: !!isSelected,
+            isDisabled: !!isDisabled,
+            placeholder: !!placeholder,
+            customProperties: customProperties
+          });
+        }
+      } else {
+        _this._addChoice({
+          value: value,
+          label: label,
+          isSelected: !!choice.selected,
+          isDisabled: !!choice.disabled,
+          placeholder: !!choice.placeholder,
+          customProperties: customProperties
+        });
+      }
+    });
+  };
+  Choices.prototype._addPredefinedItems = function (items) {
+    var _this = this;
+    items.forEach(function (item) {
+      if (typeof item === 'object' && item.value) {
+        _this._addItem({
+          value: item.value,
+          label: item.label,
+          choiceId: item.id,
+          customProperties: item.customProperties,
+          placeholder: item.placeholder
+        });
+      }
+      if (typeof item === 'string') {
+        _this._addItem({
+          value: item
+        });
+      }
+    });
+  };
+  Choices.prototype._setChoiceOrItem = function (item) {
+    var _this = this;
+    var itemType = (0, utils_1.getType)(item).toLowerCase();
+    var handleType = {
+      object: function () {
+        if (!item.value) {
+          return;
+        }
+        // If we are dealing with a select input, we need to create an option first
+        // that is then selected. For text inputs we can just add items normally.
+        if (!_this._isTextElement) {
+          _this._addChoice({
+            value: item.value,
+            label: item.label,
+            isSelected: true,
+            isDisabled: false,
+            customProperties: item.customProperties,
+            placeholder: item.placeholder
+          });
+        } else {
+          _this._addItem({
+            value: item.value,
+            label: item.label,
+            choiceId: item.id,
+            customProperties: item.customProperties,
+            placeholder: item.placeholder
+          });
+        }
+      },
+      string: function () {
+        if (!_this._isTextElement) {
+          _this._addChoice({
+            value: item,
+            label: item,
+            isSelected: true,
+            isDisabled: false
+          });
+        } else {
+          _this._addItem({
+            value: item
+          });
+        }
+      }
+    };
+    handleType[itemType]();
+  };
+  Choices.prototype._findAndSelectChoiceByValue = function (value) {
+    var _this = this;
+    var choices = this._store.choices;
+    // Check 'value' property exists and the choice isn't already selected
+    var foundChoice = choices.find(function (choice) {
+      return _this.config.valueComparer(choice.value, value);
+    });
+    if (foundChoice && !foundChoice.selected) {
+      this._addItem({
+        value: foundChoice.value,
+        label: foundChoice.label,
+        choiceId: foundChoice.id,
+        groupId: foundChoice.groupId,
+        customProperties: foundChoice.customProperties,
+        placeholder: foundChoice.placeholder,
+        keyCode: foundChoice.keyCode
+      });
+    }
+  };
+  Choices.prototype._generatePlaceholderValue = function () {
+    if (this._isSelectElement && this.passedElement.placeholderOption) {
+      var placeholderOption = this.passedElement.placeholderOption;
+      return placeholderOption ? placeholderOption.text : null;
+    }
+    var _a = this.config,
+      placeholder = _a.placeholder,
+      placeholderValue = _a.placeholderValue;
+    var dataset = this.passedElement.element.dataset;
+    if (placeholder) {
+      if (placeholderValue) {
+        return placeholderValue;
+      }
+      if (dataset.placeholder) {
+        return dataset.placeholder;
+      }
+    }
+    return null;
+  };
+  return Choices;
+}();
+exports["default"] = Choices;
+
+/***/ }),
+
+/***/ 613:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var utils_1 = __webpack_require__(799);
+var constants_1 = __webpack_require__(883);
+var Container = /** @class */function () {
+  function Container(_a) {
+    var element = _a.element,
+      type = _a.type,
+      classNames = _a.classNames,
+      position = _a.position;
+    this.element = element;
+    this.classNames = classNames;
+    this.type = type;
+    this.position = position;
+    this.isOpen = false;
+    this.isFlipped = false;
+    this.isFocussed = false;
+    this.isDisabled = false;
+    this.isLoading = false;
+    this._onFocus = this._onFocus.bind(this);
+    this._onBlur = this._onBlur.bind(this);
+  }
+  Container.prototype.addEventListeners = function () {
+    this.element.addEventListener('focus', this._onFocus);
+    this.element.addEventListener('blur', this._onBlur);
+  };
+  Container.prototype.removeEventListeners = function () {
+    this.element.removeEventListener('focus', this._onFocus);
+    this.element.removeEventListener('blur', this._onBlur);
+  };
+  /**
+   * Determine whether container should be flipped based on passed
+   * dropdown position
+   */
+  Container.prototype.shouldFlip = function (dropdownPos) {
+    if (typeof dropdownPos !== 'number') {
+      return false;
+    }
+    // If flip is enabled and the dropdown bottom position is
+    // greater than the window height flip the dropdown.
+    var shouldFlip = false;
+    if (this.position === 'auto') {
+      shouldFlip = !window.matchMedia("(min-height: ".concat(dropdownPos + 1, "px)")).matches;
+    } else if (this.position === 'top') {
+      shouldFlip = true;
+    }
+    return shouldFlip;
+  };
+  Container.prototype.setActiveDescendant = function (activeDescendantID) {
+    this.element.setAttribute('aria-activedescendant', activeDescendantID);
+  };
+  Container.prototype.removeActiveDescendant = function () {
+    this.element.removeAttribute('aria-activedescendant');
+  };
+  Container.prototype.open = function (dropdownPos) {
+    this.element.classList.add(this.classNames.openState);
+    this.element.setAttribute('aria-expanded', 'true');
+    this.isOpen = true;
+    if (this.shouldFlip(dropdownPos)) {
+      this.element.classList.add(this.classNames.flippedState);
+      this.isFlipped = true;
+    }
+  };
+  Container.prototype.close = function () {
+    this.element.classList.remove(this.classNames.openState);
+    this.element.setAttribute('aria-expanded', 'false');
+    this.removeActiveDescendant();
+    this.isOpen = false;
+    // A dropdown flips if it does not have space within the page
+    if (this.isFlipped) {
+      this.element.classList.remove(this.classNames.flippedState);
+      this.isFlipped = false;
+    }
+  };
+  Container.prototype.focus = function () {
+    if (!this.isFocussed) {
+      this.element.focus();
+    }
+  };
+  Container.prototype.addFocusState = function () {
+    this.element.classList.add(this.classNames.focusState);
+  };
+  Container.prototype.removeFocusState = function () {
+    this.element.classList.remove(this.classNames.focusState);
+  };
+  Container.prototype.enable = function () {
+    this.element.classList.remove(this.classNames.disabledState);
+    this.element.removeAttribute('aria-disabled');
+    if (this.type === constants_1.SELECT_ONE_TYPE) {
+      this.element.setAttribute('tabindex', '0');
+    }
+    this.isDisabled = false;
+  };
+  Container.prototype.disable = function () {
+    this.element.classList.add(this.classNames.disabledState);
+    this.element.setAttribute('aria-disabled', 'true');
+    if (this.type === constants_1.SELECT_ONE_TYPE) {
+      this.element.setAttribute('tabindex', '-1');
+    }
+    this.isDisabled = true;
+  };
+  Container.prototype.wrap = function (element) {
+    (0, utils_1.wrap)(element, this.element);
+  };
+  Container.prototype.unwrap = function (element) {
+    if (this.element.parentNode) {
+      // Move passed element outside this element
+      this.element.parentNode.insertBefore(element, this.element);
+      // Remove this element
+      this.element.parentNode.removeChild(this.element);
+    }
+  };
+  Container.prototype.addLoadingState = function () {
+    this.element.classList.add(this.classNames.loadingState);
+    this.element.setAttribute('aria-busy', 'true');
+    this.isLoading = true;
+  };
+  Container.prototype.removeLoadingState = function () {
+    this.element.classList.remove(this.classNames.loadingState);
+    this.element.removeAttribute('aria-busy');
+    this.isLoading = false;
+  };
+  Container.prototype._onFocus = function () {
+    this.isFocussed = true;
+  };
+  Container.prototype._onBlur = function () {
+    this.isFocussed = false;
+  };
+  return Container;
+}();
+exports["default"] = Container;
+
+/***/ }),
+
+/***/ 217:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var Dropdown = /** @class */function () {
+  function Dropdown(_a) {
+    var element = _a.element,
+      type = _a.type,
+      classNames = _a.classNames;
+    this.element = element;
+    this.classNames = classNames;
+    this.type = type;
+    this.isActive = false;
+  }
+  Object.defineProperty(Dropdown.prototype, "distanceFromTopWindow", {
+    /**
+     * Bottom position of dropdown in viewport coordinates
+     */
+    get: function () {
+      return this.element.getBoundingClientRect().bottom;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Dropdown.prototype.getChild = function (selector) {
+    return this.element.querySelector(selector);
+  };
+  /**
+   * Show dropdown to user by adding active state class
+   */
+  Dropdown.prototype.show = function () {
+    this.element.classList.add(this.classNames.activeState);
+    this.element.setAttribute('aria-expanded', 'true');
+    this.isActive = true;
+    return this;
+  };
+  /**
+   * Hide dropdown from user
+   */
+  Dropdown.prototype.hide = function () {
+    this.element.classList.remove(this.classNames.activeState);
+    this.element.setAttribute('aria-expanded', 'false');
+    this.isActive = false;
+    return this;
+  };
+  return Dropdown;
+}();
+exports["default"] = Dropdown;
+
+/***/ }),
+
+/***/ 520:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+var __importDefault = this && this.__importDefault || function (mod) {
+  return mod && mod.__esModule ? mod : {
+    "default": mod
+  };
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.WrappedSelect = exports.WrappedInput = exports.List = exports.Input = exports.Container = exports.Dropdown = void 0;
+var dropdown_1 = __importDefault(__webpack_require__(217));
+exports.Dropdown = dropdown_1.default;
+var container_1 = __importDefault(__webpack_require__(613));
+exports.Container = container_1.default;
+var input_1 = __importDefault(__webpack_require__(11));
+exports.Input = input_1.default;
+var list_1 = __importDefault(__webpack_require__(624));
+exports.List = list_1.default;
+var wrapped_input_1 = __importDefault(__webpack_require__(541));
+exports.WrappedInput = wrapped_input_1.default;
+var wrapped_select_1 = __importDefault(__webpack_require__(982));
+exports.WrappedSelect = wrapped_select_1.default;
+
+/***/ }),
+
+/***/ 11:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var utils_1 = __webpack_require__(799);
+var constants_1 = __webpack_require__(883);
+var Input = /** @class */function () {
+  function Input(_a) {
+    var element = _a.element,
+      type = _a.type,
+      classNames = _a.classNames,
+      preventPaste = _a.preventPaste;
+    this.element = element;
+    this.type = type;
+    this.classNames = classNames;
+    this.preventPaste = preventPaste;
+    this.isFocussed = this.element.isEqualNode(document.activeElement);
+    this.isDisabled = element.disabled;
+    this._onPaste = this._onPaste.bind(this);
+    this._onInput = this._onInput.bind(this);
+    this._onFocus = this._onFocus.bind(this);
+    this._onBlur = this._onBlur.bind(this);
+  }
+  Object.defineProperty(Input.prototype, "placeholder", {
+    set: function (placeholder) {
+      this.element.placeholder = placeholder;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Input.prototype, "value", {
+    get: function () {
+      return (0, utils_1.sanitise)(this.element.value);
+    },
+    set: function (value) {
+      this.element.value = value;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Input.prototype, "rawValue", {
+    get: function () {
+      return this.element.value;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Input.prototype.addEventListeners = function () {
+    this.element.addEventListener('paste', this._onPaste);
+    this.element.addEventListener('input', this._onInput, {
+      passive: true
+    });
+    this.element.addEventListener('focus', this._onFocus, {
+      passive: true
+    });
+    this.element.addEventListener('blur', this._onBlur, {
+      passive: true
+    });
+  };
+  Input.prototype.removeEventListeners = function () {
+    this.element.removeEventListener('input', this._onInput);
+    this.element.removeEventListener('paste', this._onPaste);
+    this.element.removeEventListener('focus', this._onFocus);
+    this.element.removeEventListener('blur', this._onBlur);
+  };
+  Input.prototype.enable = function () {
+    this.element.removeAttribute('disabled');
+    this.isDisabled = false;
+  };
+  Input.prototype.disable = function () {
+    this.element.setAttribute('disabled', '');
+    this.isDisabled = true;
+  };
+  Input.prototype.focus = function () {
+    if (!this.isFocussed) {
+      this.element.focus();
+    }
+  };
+  Input.prototype.blur = function () {
+    if (this.isFocussed) {
+      this.element.blur();
+    }
+  };
+  Input.prototype.clear = function (setWidth) {
+    if (setWidth === void 0) {
+      setWidth = true;
+    }
+    if (this.element.value) {
+      this.element.value = '';
+    }
+    if (setWidth) {
+      this.setWidth();
+    }
+    return this;
+  };
+  /**
+   * Set the correct input width based on placeholder
+   * value or input value
+   */
+  Input.prototype.setWidth = function () {
+    // Resize input to contents or placeholder
+    var _a = this.element,
+      style = _a.style,
+      value = _a.value,
+      placeholder = _a.placeholder;
+    style.minWidth = "".concat(placeholder.length + 1, "ch");
+    style.width = "".concat(value.length + 1, "ch");
+  };
+  Input.prototype.setActiveDescendant = function (activeDescendantID) {
+    this.element.setAttribute('aria-activedescendant', activeDescendantID);
+  };
+  Input.prototype.removeActiveDescendant = function () {
+    this.element.removeAttribute('aria-activedescendant');
+  };
+  Input.prototype._onInput = function () {
+    if (this.type !== constants_1.SELECT_ONE_TYPE) {
+      this.setWidth();
+    }
+  };
+  Input.prototype._onPaste = function (event) {
+    if (this.preventPaste) {
+      event.preventDefault();
+    }
+  };
+  Input.prototype._onFocus = function () {
+    this.isFocussed = true;
+  };
+  Input.prototype._onBlur = function () {
+    this.isFocussed = false;
+  };
+  return Input;
+}();
+exports["default"] = Input;
+
+/***/ }),
+
+/***/ 624:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var constants_1 = __webpack_require__(883);
+var List = /** @class */function () {
+  function List(_a) {
+    var element = _a.element;
+    this.element = element;
+    this.scrollPos = this.element.scrollTop;
+    this.height = this.element.offsetHeight;
+  }
+  List.prototype.clear = function () {
+    this.element.innerHTML = '';
+  };
+  List.prototype.append = function (node) {
+    this.element.appendChild(node);
+  };
+  List.prototype.getChild = function (selector) {
+    return this.element.querySelector(selector);
+  };
+  List.prototype.hasChildren = function () {
+    return this.element.hasChildNodes();
+  };
+  List.prototype.scrollToTop = function () {
+    this.element.scrollTop = 0;
+  };
+  List.prototype.scrollToChildElement = function (element, direction) {
+    var _this = this;
+    if (!element) {
+      return;
+    }
+    var listHeight = this.element.offsetHeight;
+    // Scroll position of dropdown
+    var listScrollPosition = this.element.scrollTop + listHeight;
+    var elementHeight = element.offsetHeight;
+    // Distance from bottom of element to top of parent
+    var elementPos = element.offsetTop + elementHeight;
+    // Difference between the element and scroll position
+    var destination = direction > 0 ? this.element.scrollTop + elementPos - listScrollPosition : element.offsetTop;
+    requestAnimationFrame(function () {
+      _this._animateScroll(destination, direction);
+    });
+  };
+  List.prototype._scrollDown = function (scrollPos, strength, destination) {
+    var easing = (destination - scrollPos) / strength;
+    var distance = easing > 1 ? easing : 1;
+    this.element.scrollTop = scrollPos + distance;
+  };
+  List.prototype._scrollUp = function (scrollPos, strength, destination) {
+    var easing = (scrollPos - destination) / strength;
+    var distance = easing > 1 ? easing : 1;
+    this.element.scrollTop = scrollPos - distance;
+  };
+  List.prototype._animateScroll = function (destination, direction) {
+    var _this = this;
+    var strength = constants_1.SCROLLING_SPEED;
+    var choiceListScrollTop = this.element.scrollTop;
+    var continueAnimation = false;
+    if (direction > 0) {
+      this._scrollDown(choiceListScrollTop, strength, destination);
+      if (choiceListScrollTop < destination) {
+        continueAnimation = true;
+      }
+    } else {
+      this._scrollUp(choiceListScrollTop, strength, destination);
+      if (choiceListScrollTop > destination) {
+        continueAnimation = true;
+      }
+    }
+    if (continueAnimation) {
+      requestAnimationFrame(function () {
+        _this._animateScroll(destination, direction);
+      });
+    }
+  };
+  return List;
+}();
+exports["default"] = List;
+
+/***/ }),
+
+/***/ 730:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var utils_1 = __webpack_require__(799);
+var WrappedElement = /** @class */function () {
+  function WrappedElement(_a) {
+    var element = _a.element,
+      classNames = _a.classNames;
+    this.element = element;
+    this.classNames = classNames;
+    if (!(element instanceof HTMLInputElement) && !(element instanceof HTMLSelectElement)) {
+      throw new TypeError('Invalid element passed');
+    }
+    this.isDisabled = false;
+  }
+  Object.defineProperty(WrappedElement.prototype, "isActive", {
+    get: function () {
+      return this.element.dataset.choice === 'active';
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(WrappedElement.prototype, "dir", {
+    get: function () {
+      return this.element.dir;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(WrappedElement.prototype, "value", {
+    get: function () {
+      return this.element.value;
+    },
+    set: function (value) {
+      // you must define setter here otherwise it will be readonly property
+      this.element.value = value;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  WrappedElement.prototype.conceal = function () {
+    // Hide passed input
+    this.element.classList.add(this.classNames.input);
+    this.element.hidden = true;
+    // Remove element from tab index
+    this.element.tabIndex = -1;
+    // Backup original styles if any
+    var origStyle = this.element.getAttribute('style');
+    if (origStyle) {
+      this.element.setAttribute('data-choice-orig-style', origStyle);
+    }
+    this.element.setAttribute('data-choice', 'active');
+  };
+  WrappedElement.prototype.reveal = function () {
+    // Reinstate passed element
+    this.element.classList.remove(this.classNames.input);
+    this.element.hidden = false;
+    this.element.removeAttribute('tabindex');
+    // Recover original styles if any
+    var origStyle = this.element.getAttribute('data-choice-orig-style');
+    if (origStyle) {
+      this.element.removeAttribute('data-choice-orig-style');
+      this.element.setAttribute('style', origStyle);
+    } else {
+      this.element.removeAttribute('style');
+    }
+    this.element.removeAttribute('data-choice');
+    // Re-assign values - this is weird, I know
+    // @todo Figure out why we need to do this
+    this.element.value = this.element.value; // eslint-disable-line no-self-assign
+  };
+
+  WrappedElement.prototype.enable = function () {
+    this.element.removeAttribute('disabled');
+    this.element.disabled = false;
+    this.isDisabled = false;
+  };
+  WrappedElement.prototype.disable = function () {
+    this.element.setAttribute('disabled', '');
+    this.element.disabled = true;
+    this.isDisabled = true;
+  };
+  WrappedElement.prototype.triggerEvent = function (eventType, data) {
+    (0, utils_1.dispatchEvent)(this.element, eventType, data);
+  };
+  return WrappedElement;
+}();
+exports["default"] = WrappedElement;
+
+/***/ }),
+
+/***/ 541:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+var __extends = this && this.__extends || function () {
+  var extendStatics = function (d, b) {
+    extendStatics = Object.setPrototypeOf || {
+      __proto__: []
+    } instanceof Array && function (d, b) {
+      d.__proto__ = b;
+    } || function (d, b) {
+      for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p];
+    };
+    return extendStatics(d, b);
+  };
+  return function (d, b) {
+    if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+    extendStatics(d, b);
+    function __() {
+      this.constructor = d;
+    }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+  };
+}();
+var __importDefault = this && this.__importDefault || function (mod) {
+  return mod && mod.__esModule ? mod : {
+    "default": mod
+  };
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var wrapped_element_1 = __importDefault(__webpack_require__(730));
+var WrappedInput = /** @class */function (_super) {
+  __extends(WrappedInput, _super);
+  function WrappedInput(_a) {
+    var element = _a.element,
+      classNames = _a.classNames,
+      delimiter = _a.delimiter;
+    var _this = _super.call(this, {
+      element: element,
+      classNames: classNames
+    }) || this;
+    _this.delimiter = delimiter;
+    return _this;
+  }
+  Object.defineProperty(WrappedInput.prototype, "value", {
+    get: function () {
+      return this.element.value;
+    },
+    set: function (value) {
+      this.element.setAttribute('value', value);
+      this.element.value = value;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  return WrappedInput;
+}(wrapped_element_1.default);
+exports["default"] = WrappedInput;
+
+/***/ }),
+
+/***/ 982:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+var __extends = this && this.__extends || function () {
+  var extendStatics = function (d, b) {
+    extendStatics = Object.setPrototypeOf || {
+      __proto__: []
+    } instanceof Array && function (d, b) {
+      d.__proto__ = b;
+    } || function (d, b) {
+      for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p];
+    };
+    return extendStatics(d, b);
+  };
+  return function (d, b) {
+    if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+    extendStatics(d, b);
+    function __() {
+      this.constructor = d;
+    }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+  };
+}();
+var __importDefault = this && this.__importDefault || function (mod) {
+  return mod && mod.__esModule ? mod : {
+    "default": mod
+  };
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var wrapped_element_1 = __importDefault(__webpack_require__(730));
+var WrappedSelect = /** @class */function (_super) {
+  __extends(WrappedSelect, _super);
+  function WrappedSelect(_a) {
+    var element = _a.element,
+      classNames = _a.classNames,
+      template = _a.template;
+    var _this = _super.call(this, {
+      element: element,
+      classNames: classNames
+    }) || this;
+    _this.template = template;
+    return _this;
+  }
+  Object.defineProperty(WrappedSelect.prototype, "placeholderOption", {
+    get: function () {
+      return this.element.querySelector('option[value=""]') ||
+      // Backward compatibility layer for the non-standard placeholder attribute supported in older versions.
+      this.element.querySelector('option[placeholder]');
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(WrappedSelect.prototype, "optionGroups", {
+    get: function () {
+      return Array.from(this.element.getElementsByTagName('OPTGROUP'));
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(WrappedSelect.prototype, "options", {
+    get: function () {
+      return Array.from(this.element.options);
+    },
+    set: function (options) {
+      var _this = this;
+      var fragment = document.createDocumentFragment();
+      var addOptionToFragment = function (data) {
+        // Create a standard select option
+        var option = _this.template(data);
+        // Append it to fragment
+        fragment.appendChild(option);
+      };
+      // Add each list item to list
+      options.forEach(function (optionData) {
+        return addOptionToFragment(optionData);
+      });
+      this.appendDocFragment(fragment);
+    },
+    enumerable: false,
+    configurable: true
+  });
+  WrappedSelect.prototype.appendDocFragment = function (fragment) {
+    this.element.innerHTML = '';
+    this.element.appendChild(fragment);
+  };
+  return WrappedSelect;
+}(wrapped_element_1.default);
+exports["default"] = WrappedSelect;
+
+/***/ }),
+
+/***/ 883:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.SCROLLING_SPEED = exports.SELECT_MULTIPLE_TYPE = exports.SELECT_ONE_TYPE = exports.TEXT_TYPE = exports.KEY_CODES = exports.ACTION_TYPES = exports.EVENTS = void 0;
+exports.EVENTS = {
+  showDropdown: 'showDropdown',
+  hideDropdown: 'hideDropdown',
+  change: 'change',
+  choice: 'choice',
+  search: 'search',
+  addItem: 'addItem',
+  removeItem: 'removeItem',
+  highlightItem: 'highlightItem',
+  highlightChoice: 'highlightChoice',
+  unhighlightItem: 'unhighlightItem'
+};
+exports.ACTION_TYPES = {
+  ADD_CHOICE: 'ADD_CHOICE',
+  FILTER_CHOICES: 'FILTER_CHOICES',
+  ACTIVATE_CHOICES: 'ACTIVATE_CHOICES',
+  CLEAR_CHOICES: 'CLEAR_CHOICES',
+  ADD_GROUP: 'ADD_GROUP',
+  ADD_ITEM: 'ADD_ITEM',
+  REMOVE_ITEM: 'REMOVE_ITEM',
+  HIGHLIGHT_ITEM: 'HIGHLIGHT_ITEM',
+  CLEAR_ALL: 'CLEAR_ALL',
+  RESET_TO: 'RESET_TO',
+  SET_IS_LOADING: 'SET_IS_LOADING'
+};
+exports.KEY_CODES = {
+  BACK_KEY: 46,
+  DELETE_KEY: 8,
+  ENTER_KEY: 13,
+  A_KEY: 65,
+  ESC_KEY: 27,
+  UP_KEY: 38,
+  DOWN_KEY: 40,
+  PAGE_UP_KEY: 33,
+  PAGE_DOWN_KEY: 34
+};
+exports.TEXT_TYPE = 'text';
+exports.SELECT_ONE_TYPE = 'select-one';
+exports.SELECT_MULTIPLE_TYPE = 'select-multiple';
+exports.SCROLLING_SPEED = 4;
+
+/***/ }),
+
+/***/ 789:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.DEFAULT_CONFIG = exports.DEFAULT_CLASSNAMES = void 0;
+var utils_1 = __webpack_require__(799);
+exports.DEFAULT_CLASSNAMES = {
+  containerOuter: 'choices',
+  containerInner: 'choices__inner',
+  input: 'choices__input',
+  inputCloned: 'choices__input--cloned',
+  list: 'choices__list',
+  listItems: 'choices__list--multiple',
+  listSingle: 'choices__list--single',
+  listDropdown: 'choices__list--dropdown',
+  item: 'choices__item',
+  itemSelectable: 'choices__item--selectable',
+  itemDisabled: 'choices__item--disabled',
+  itemChoice: 'choices__item--choice',
+  placeholder: 'choices__placeholder',
+  group: 'choices__group',
+  groupHeading: 'choices__heading',
+  button: 'choices__button',
+  activeState: 'is-active',
+  focusState: 'is-focused',
+  openState: 'is-open',
+  disabledState: 'is-disabled',
+  highlightedState: 'is-highlighted',
+  selectedState: 'is-selected',
+  flippedState: 'is-flipped',
+  loadingState: 'is-loading',
+  noResults: 'has-no-results',
+  noChoices: 'has-no-choices'
+};
+exports.DEFAULT_CONFIG = {
+  items: [],
+  choices: [],
+  silent: false,
+  renderChoiceLimit: -1,
+  maxItemCount: -1,
+  addItems: true,
+  addItemFilter: null,
+  removeItems: true,
+  removeItemButton: false,
+  editItems: false,
+  allowHTML: true,
+  duplicateItemsAllowed: true,
+  delimiter: ',',
+  paste: true,
+  searchEnabled: true,
+  searchChoices: true,
+  searchFloor: 1,
+  searchResultLimit: 4,
+  searchFields: ['label', 'value'],
+  position: 'auto',
+  resetScrollPosition: true,
+  shouldSort: true,
+  shouldSortItems: false,
+  sorter: utils_1.sortByAlpha,
+  placeholder: true,
+  placeholderValue: null,
+  searchPlaceholderValue: null,
+  prependValue: null,
+  appendValue: null,
+  renderSelectedChoices: 'auto',
+  loadingText: 'Loading...',
+  noResultsText: 'No results found',
+  noChoicesText: 'No choices to choose from',
+  itemSelectText: 'Press to select',
+  uniqueItemText: 'Only unique values can be added',
+  customAddItemText: 'Only values matching specific conditions can be added',
+  addItemText: function (value) {
+    return "Press Enter to add <b>\"".concat((0, utils_1.sanitise)(value), "\"</b>");
+  },
+  maxItemText: function (maxItemCount) {
+    return "Only ".concat(maxItemCount, " values can be added");
+  },
+  valueComparer: function (value1, value2) {
+    return value1 === value2;
+  },
+  fuseOptions: {
+    includeScore: true
+  },
+  labelId: '',
+  callbackOnInit: null,
+  callbackOnCreateTemplates: null,
+  classNames: exports.DEFAULT_CLASSNAMES
+};
+
+/***/ }),
+
+/***/ 18:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 978:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 948:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 359:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 285:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 533:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 187:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+var __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {
+  if (k2 === undefined) k2 = k;
+  var desc = Object.getOwnPropertyDescriptor(m, k);
+  if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+    desc = {
+      enumerable: true,
+      get: function () {
+        return m[k];
+      }
+    };
+  }
+  Object.defineProperty(o, k2, desc);
+} : function (o, m, k, k2) {
+  if (k2 === undefined) k2 = k;
+  o[k2] = m[k];
+});
+var __exportStar = this && this.__exportStar || function (m, exports) {
+  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+__exportStar(__webpack_require__(18), exports);
+__exportStar(__webpack_require__(978), exports);
+__exportStar(__webpack_require__(948), exports);
+__exportStar(__webpack_require__(359), exports);
+__exportStar(__webpack_require__(285), exports);
+__exportStar(__webpack_require__(533), exports);
+__exportStar(__webpack_require__(287), exports);
+__exportStar(__webpack_require__(132), exports);
+__exportStar(__webpack_require__(837), exports);
+__exportStar(__webpack_require__(598), exports);
+__exportStar(__webpack_require__(369), exports);
+__exportStar(__webpack_require__(37), exports);
+__exportStar(__webpack_require__(47), exports);
+__exportStar(__webpack_require__(923), exports);
+__exportStar(__webpack_require__(876), exports);
+
+/***/ }),
+
+/***/ 287:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 132:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 837:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 598:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 37:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 369:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 47:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 923:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 876:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+
+/***/ }),
+
+/***/ 799:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.parseCustomProperties = exports.diff = exports.cloneObject = exports.existsInArray = exports.dispatchEvent = exports.sortByScore = exports.sortByAlpha = exports.strToEl = exports.sanitise = exports.isScrolledIntoView = exports.getAdjacentEl = exports.wrap = exports.isType = exports.getType = exports.generateId = exports.generateChars = exports.getRandomNumber = void 0;
+var getRandomNumber = function (min, max) {
+  return Math.floor(Math.random() * (max - min) + min);
+};
+exports.getRandomNumber = getRandomNumber;
+var generateChars = function (length) {
+  return Array.from({
+    length: length
+  }, function () {
+    return (0, exports.getRandomNumber)(0, 36).toString(36);
+  }).join('');
+};
+exports.generateChars = generateChars;
+var generateId = function (element, prefix) {
+  var id = element.id || element.name && "".concat(element.name, "-").concat((0, exports.generateChars)(2)) || (0, exports.generateChars)(4);
+  id = id.replace(/(:|\.|\[|\]|,)/g, '');
+  id = "".concat(prefix, "-").concat(id);
+  return id;
+};
+exports.generateId = generateId;
+var getType = function (obj) {
+  return Object.prototype.toString.call(obj).slice(8, -1);
+};
+exports.getType = getType;
+var isType = function (type, obj) {
+  return obj !== undefined && obj !== null && (0, exports.getType)(obj) === type;
+};
+exports.isType = isType;
+var wrap = function (element, wrapper) {
+  if (wrapper === void 0) {
+    wrapper = document.createElement('div');
+  }
+  if (element.parentNode) {
+    if (element.nextSibling) {
+      element.parentNode.insertBefore(wrapper, element.nextSibling);
+    } else {
+      element.parentNode.appendChild(wrapper);
+    }
+  }
+  return wrapper.appendChild(element);
+};
+exports.wrap = wrap;
+var getAdjacentEl = function (startEl, selector, direction) {
+  if (direction === void 0) {
+    direction = 1;
+  }
+  var prop = "".concat(direction > 0 ? 'next' : 'previous', "ElementSibling");
+  var sibling = startEl[prop];
+  while (sibling) {
+    if (sibling.matches(selector)) {
+      return sibling;
+    }
+    sibling = sibling[prop];
+  }
+  return sibling;
+};
+exports.getAdjacentEl = getAdjacentEl;
+var isScrolledIntoView = function (element, parent, direction) {
+  if (direction === void 0) {
+    direction = 1;
+  }
+  if (!element) {
+    return false;
+  }
+  var isVisible;
+  if (direction > 0) {
+    // In view from bottom
+    isVisible = parent.scrollTop + parent.offsetHeight >= element.offsetTop + element.offsetHeight;
+  } else {
+    // In view from top
+    isVisible = element.offsetTop >= parent.scrollTop;
+  }
+  return isVisible;
+};
+exports.isScrolledIntoView = isScrolledIntoView;
+var sanitise = function (value) {
+  if (typeof value !== 'string') {
+    return value;
+  }
+  return value.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
+};
+exports.sanitise = sanitise;
+exports.strToEl = function () {
+  var tmpEl = document.createElement('div');
+  return function (str) {
+    var cleanedInput = str.trim();
+    tmpEl.innerHTML = cleanedInput;
+    var firldChild = tmpEl.children[0];
+    while (tmpEl.firstChild) {
+      tmpEl.removeChild(tmpEl.firstChild);
+    }
+    return firldChild;
+  };
+}();
+var sortByAlpha = function (_a, _b) {
+  var value = _a.value,
+    _c = _a.label,
+    label = _c === void 0 ? value : _c;
+  var value2 = _b.value,
+    _d = _b.label,
+    label2 = _d === void 0 ? value2 : _d;
+  return label.localeCompare(label2, [], {
+    sensitivity: 'base',
+    ignorePunctuation: true,
+    numeric: true
+  });
+};
+exports.sortByAlpha = sortByAlpha;
+var sortByScore = function (a, b) {
+  var _a = a.score,
+    scoreA = _a === void 0 ? 0 : _a;
+  var _b = b.score,
+    scoreB = _b === void 0 ? 0 : _b;
+  return scoreA - scoreB;
+};
+exports.sortByScore = sortByScore;
+var dispatchEvent = function (element, type, customArgs) {
+  if (customArgs === void 0) {
+    customArgs = null;
+  }
+  var event = new CustomEvent(type, {
+    detail: customArgs,
+    bubbles: true,
+    cancelable: true
+  });
+  return element.dispatchEvent(event);
+};
+exports.dispatchEvent = dispatchEvent;
+var existsInArray = function (array, value, key) {
+  if (key === void 0) {
+    key = 'value';
+  }
+  return array.some(function (item) {
+    if (typeof value === 'string') {
+      return item[key] === value.trim();
+    }
+    return item[key] === value;
+  });
+};
+exports.existsInArray = existsInArray;
+var cloneObject = function (obj) {
+  return JSON.parse(JSON.stringify(obj));
+};
+exports.cloneObject = cloneObject;
+/**
+ * Returns an array of keys present on the first but missing on the second object
+ */
+var diff = function (a, b) {
+  var aKeys = Object.keys(a).sort();
+  var bKeys = Object.keys(b).sort();
+  return aKeys.filter(function (i) {
+    return bKeys.indexOf(i) < 0;
+  });
+};
+exports.diff = diff;
+var parseCustomProperties = function (customProperties) {
+  if (typeof customProperties !== 'undefined') {
+    try {
+      return JSON.parse(customProperties);
+    } catch (e) {
+      return customProperties;
+    }
+  }
+  return {};
+};
+exports.parseCustomProperties = parseCustomProperties;
+
+/***/ }),
+
+/***/ 273:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+var __spreadArray = this && this.__spreadArray || function (to, from, pack) {
+  if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+    if (ar || !(i in from)) {
+      if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+      ar[i] = from[i];
+    }
+  }
+  return to.concat(ar || Array.prototype.slice.call(from));
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.defaultState = void 0;
+exports.defaultState = [];
+function choices(state, action) {
+  if (state === void 0) {
+    state = exports.defaultState;
+  }
+  if (action === void 0) {
+    action = {};
+  }
+  switch (action.type) {
+    case 'ADD_CHOICE':
+      {
+        var addChoiceAction = action;
+        var choice = {
+          id: addChoiceAction.id,
+          elementId: addChoiceAction.elementId,
+          groupId: addChoiceAction.groupId,
+          value: addChoiceAction.value,
+          label: addChoiceAction.label || addChoiceAction.value,
+          disabled: addChoiceAction.disabled || false,
+          selected: false,
+          active: true,
+          score: 9999,
+          customProperties: addChoiceAction.customProperties,
+          placeholder: addChoiceAction.placeholder || false
+        };
+        /*
+          A disabled choice appears in the choice dropdown but cannot be selected
+          A selected choice has been added to the passed input's value (added as an item)
+          An active choice appears within the choice dropdown
+        */
+        return __spreadArray(__spreadArray([], state, true), [choice], false);
+      }
+    case 'ADD_ITEM':
+      {
+        var addItemAction_1 = action;
+        // When an item is added and it has an associated choice,
+        // we want to disable it so it can't be chosen again
+        if (addItemAction_1.choiceId > -1) {
+          return state.map(function (obj) {
+            var choice = obj;
+            if (choice.id === parseInt("".concat(addItemAction_1.choiceId), 10)) {
+              choice.selected = true;
+            }
+            return choice;
+          });
+        }
+        return state;
+      }
+    case 'REMOVE_ITEM':
+      {
+        var removeItemAction_1 = action;
+        // When an item is removed and it has an associated choice,
+        // we want to re-enable it so it can be chosen again
+        if (removeItemAction_1.choiceId && removeItemAction_1.choiceId > -1) {
+          return state.map(function (obj) {
+            var choice = obj;
+            if (choice.id === parseInt("".concat(removeItemAction_1.choiceId), 10)) {
+              choice.selected = false;
+            }
+            return choice;
+          });
+        }
+        return state;
+      }
+    case 'FILTER_CHOICES':
+      {
+        var filterChoicesAction_1 = action;
+        return state.map(function (obj) {
+          var choice = obj;
+          // Set active state based on whether choice is
+          // within filtered results
+          choice.active = filterChoicesAction_1.results.some(function (_a) {
+            var item = _a.item,
+              score = _a.score;
+            if (item.id === choice.id) {
+              choice.score = score;
+              return true;
+            }
+            return false;
+          });
+          return choice;
+        });
+      }
+    case 'ACTIVATE_CHOICES':
+      {
+        var activateChoicesAction_1 = action;
+        return state.map(function (obj) {
+          var choice = obj;
+          choice.active = activateChoicesAction_1.active;
+          return choice;
+        });
+      }
+    case 'CLEAR_CHOICES':
+      {
+        return exports.defaultState;
+      }
+    default:
+      {
+        return state;
+      }
+  }
+}
+exports["default"] = choices;
+
+/***/ }),
+
+/***/ 871:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+var __spreadArray = this && this.__spreadArray || function (to, from, pack) {
+  if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+    if (ar || !(i in from)) {
+      if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+      ar[i] = from[i];
+    }
+  }
+  return to.concat(ar || Array.prototype.slice.call(from));
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.defaultState = void 0;
+exports.defaultState = [];
+function groups(state, action) {
+  if (state === void 0) {
+    state = exports.defaultState;
+  }
+  if (action === void 0) {
+    action = {};
+  }
+  switch (action.type) {
+    case 'ADD_GROUP':
+      {
+        var addGroupAction = action;
+        return __spreadArray(__spreadArray([], state, true), [{
+          id: addGroupAction.id,
+          value: addGroupAction.value,
+          active: addGroupAction.active,
+          disabled: addGroupAction.disabled
+        }], false);
+      }
+    case 'CLEAR_CHOICES':
+      {
+        return [];
+      }
+    default:
+      {
+        return state;
+      }
+  }
+}
+exports["default"] = groups;
+
+/***/ }),
+
+/***/ 655:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+var __importDefault = this && this.__importDefault || function (mod) {
+  return mod && mod.__esModule ? mod : {
+    "default": mod
+  };
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.defaultState = void 0;
+var redux_1 = __webpack_require__(791);
+var items_1 = __importDefault(__webpack_require__(52));
+var groups_1 = __importDefault(__webpack_require__(871));
+var choices_1 = __importDefault(__webpack_require__(273));
+var loading_1 = __importDefault(__webpack_require__(502));
+var utils_1 = __webpack_require__(799);
+exports.defaultState = {
+  groups: [],
+  items: [],
+  choices: [],
+  loading: false
+};
+var appReducer = (0, redux_1.combineReducers)({
+  items: items_1.default,
+  groups: groups_1.default,
+  choices: choices_1.default,
+  loading: loading_1.default
+});
+var rootReducer = function (passedState, action) {
+  var state = passedState;
+  // If we are clearing all items, groups and options we reassign
+  // state and then pass that state to our proper reducer. This isn't
+  // mutating our actual state
+  // See: http://stackoverflow.com/a/35641992
+  if (action.type === 'CLEAR_ALL') {
+    state = exports.defaultState;
+  } else if (action.type === 'RESET_TO') {
+    return (0, utils_1.cloneObject)(action.state);
+  }
+  return appReducer(state, action);
+};
+exports["default"] = rootReducer;
+
+/***/ }),
+
+/***/ 52:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+var __spreadArray = this && this.__spreadArray || function (to, from, pack) {
+  if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+    if (ar || !(i in from)) {
+      if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+      ar[i] = from[i];
+    }
+  }
+  return to.concat(ar || Array.prototype.slice.call(from));
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.defaultState = void 0;
+exports.defaultState = [];
+function items(state, action) {
+  if (state === void 0) {
+    state = exports.defaultState;
+  }
+  if (action === void 0) {
+    action = {};
+  }
+  switch (action.type) {
+    case 'ADD_ITEM':
+      {
+        var addItemAction = action;
+        // Add object to items array
+        var newState = __spreadArray(__spreadArray([], state, true), [{
+          id: addItemAction.id,
+          choiceId: addItemAction.choiceId,
+          groupId: addItemAction.groupId,
+          value: addItemAction.value,
+          label: addItemAction.label,
+          active: true,
+          highlighted: false,
+          customProperties: addItemAction.customProperties,
+          placeholder: addItemAction.placeholder || false,
+          keyCode: null
+        }], false);
+        return newState.map(function (obj) {
+          var item = obj;
+          item.highlighted = false;
+          return item;
+        });
+      }
+    case 'REMOVE_ITEM':
+      {
+        // Set item to inactive
+        return state.map(function (obj) {
+          var item = obj;
+          if (item.id === action.id) {
+            item.active = false;
+          }
+          return item;
+        });
+      }
+    case 'HIGHLIGHT_ITEM':
+      {
+        var highlightItemAction_1 = action;
+        return state.map(function (obj) {
+          var item = obj;
+          if (item.id === highlightItemAction_1.id) {
+            item.highlighted = highlightItemAction_1.highlighted;
+          }
+          return item;
+        });
+      }
+    default:
+      {
+        return state;
+      }
+  }
+}
+exports["default"] = items;
+
+/***/ }),
+
+/***/ 502:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.defaultState = void 0;
+exports.defaultState = false;
+var general = function (state, action) {
+  if (state === void 0) {
+    state = exports.defaultState;
+  }
+  if (action === void 0) {
+    action = {};
+  }
+  switch (action.type) {
+    case 'SET_IS_LOADING':
+      {
+        return action.isLoading;
+      }
+    default:
+      {
+        return state;
+      }
+  }
+};
+exports["default"] = general;
+
+/***/ }),
+
+/***/ 744:
+/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
+
+
+
+var __spreadArray = this && this.__spreadArray || function (to, from, pack) {
+  if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+    if (ar || !(i in from)) {
+      if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+      ar[i] = from[i];
+    }
+  }
+  return to.concat(ar || Array.prototype.slice.call(from));
+};
+var __importDefault = this && this.__importDefault || function (mod) {
+  return mod && mod.__esModule ? mod : {
+    "default": mod
+  };
+};
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+/* eslint-disable @typescript-eslint/no-explicit-any */
+var redux_1 = __webpack_require__(791);
+var index_1 = __importDefault(__webpack_require__(655));
+var Store = /** @class */function () {
+  function Store() {
+    this._store = (0, redux_1.createStore)(index_1.default, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
+  }
+  /**
+   * Subscribe store to function call (wrapped Redux method)
+   */
+  Store.prototype.subscribe = function (onChange) {
+    this._store.subscribe(onChange);
+  };
+  /**
+   * Dispatch event to store (wrapped Redux method)
+   */
+  Store.prototype.dispatch = function (action) {
+    this._store.dispatch(action);
+  };
+  Object.defineProperty(Store.prototype, "state", {
+    /**
+     * Get store object (wrapping Redux method)
+     */
+    get: function () {
+      return this._store.getState();
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "items", {
+    /**
+     * Get items from store
+     */
+    get: function () {
+      return this.state.items;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "activeItems", {
+    /**
+     * Get active items from store
+     */
+    get: function () {
+      return this.items.filter(function (item) {
+        return item.active === true;
+      });
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "highlightedActiveItems", {
+    /**
+     * Get highlighted items from store
+     */
+    get: function () {
+      return this.items.filter(function (item) {
+        return item.active && item.highlighted;
+      });
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "choices", {
+    /**
+     * Get choices from store
+     */
+    get: function () {
+      return this.state.choices;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "activeChoices", {
+    /**
+     * Get active choices from store
+     */
+    get: function () {
+      return this.choices.filter(function (choice) {
+        return choice.active === true;
+      });
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "selectableChoices", {
+    /**
+     * Get selectable choices from store
+     */
+    get: function () {
+      return this.choices.filter(function (choice) {
+        return choice.disabled !== true;
+      });
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "searchableChoices", {
+    /**
+     * Get choices that can be searched (excluding placeholders)
+     */
+    get: function () {
+      return this.selectableChoices.filter(function (choice) {
+        return choice.placeholder !== true;
+      });
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "placeholderChoice", {
+    /**
+     * Get placeholder choice from store
+     */
+    get: function () {
+      return __spreadArray([], this.choices, true).reverse().find(function (choice) {
+        return choice.placeholder === true;
+      });
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "groups", {
+    /**
+     * Get groups from store
+     */
+    get: function () {
+      return this.state.groups;
+    },
+    enumerable: false,
+    configurable: true
+  });
+  Object.defineProperty(Store.prototype, "activeGroups", {
+    /**
+     * Get active groups from store
+     */
+    get: function () {
+      var _a = this,
+        groups = _a.groups,
+        choices = _a.choices;
+      return groups.filter(function (group) {
+        var isActive = group.active === true && group.disabled === false;
+        var hasActiveOptions = choices.some(function (choice) {
+          return choice.active === true && choice.disabled === false;
+        });
+        return isActive && hasActiveOptions;
+      }, []);
+    },
+    enumerable: false,
+    configurable: true
+  });
+  /**
+   * Get loading state from store
+   */
+  Store.prototype.isLoading = function () {
+    return this.state.loading;
+  };
+  /**
+   * Get single choice by it's ID
+   */
+  Store.prototype.getChoiceById = function (id) {
+    return this.activeChoices.find(function (choice) {
+      return choice.id === parseInt(id, 10);
+    });
+  };
+  /**
+   * Get group by group id
+   */
+  Store.prototype.getGroupById = function (id) {
+    return this.groups.find(function (group) {
+      return group.id === id;
+    });
+  };
+  return Store;
+}();
+exports["default"] = Store;
+
+/***/ }),
+
+/***/ 686:
+/***/ (function(__unused_webpack_module, exports) {
+
+
+
+/**
+ * Helpers to create HTML elements used by Choices
+ * Can be overridden by providing `callbackOnCreateTemplates` option
+ */
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+var templates = {
+  containerOuter: function (_a, dir, isSelectElement, isSelectOneElement, searchEnabled, passedElementType, labelId) {
+    var containerOuter = _a.classNames.containerOuter;
+    var div = Object.assign(document.createElement('div'), {
+      className: containerOuter
+    });
+    div.dataset.type = passedElementType;
+    if (dir) {
+      div.dir = dir;
+    }
+    if (isSelectOneElement) {
+      div.tabIndex = 0;
+    }
+    if (isSelectElement) {
+      div.setAttribute('role', searchEnabled ? 'combobox' : 'listbox');
+      if (searchEnabled) {
+        div.setAttribute('aria-autocomplete', 'list');
+      }
+    }
+    div.setAttribute('aria-haspopup', 'true');
+    div.setAttribute('aria-expanded', 'false');
+    if (labelId) {
+      div.setAttribute('aria-labelledby', labelId);
+    }
+    return div;
+  },
+  containerInner: function (_a) {
+    var containerInner = _a.classNames.containerInner;
+    return Object.assign(document.createElement('div'), {
+      className: containerInner
+    });
+  },
+  itemList: function (_a, isSelectOneElement) {
+    var _b = _a.classNames,
+      list = _b.list,
+      listSingle = _b.listSingle,
+      listItems = _b.listItems;
+    return Object.assign(document.createElement('div'), {
+      className: "".concat(list, " ").concat(isSelectOneElement ? listSingle : listItems)
+    });
+  },
+  placeholder: function (_a, value) {
+    var _b;
+    var allowHTML = _a.allowHTML,
+      placeholder = _a.classNames.placeholder;
+    return Object.assign(document.createElement('div'), (_b = {
+      className: placeholder
+    }, _b[allowHTML ? 'innerHTML' : 'innerText'] = value, _b));
+  },
+  item: function (_a, _b, removeItemButton) {
+    var _c, _d;
+    var allowHTML = _a.allowHTML,
+      _e = _a.classNames,
+      item = _e.item,
+      button = _e.button,
+      highlightedState = _e.highlightedState,
+      itemSelectable = _e.itemSelectable,
+      placeholder = _e.placeholder;
+    var id = _b.id,
+      value = _b.value,
+      label = _b.label,
+      customProperties = _b.customProperties,
+      active = _b.active,
+      disabled = _b.disabled,
+      highlighted = _b.highlighted,
+      isPlaceholder = _b.placeholder;
+    var div = Object.assign(document.createElement('div'), (_c = {
+      className: item
+    }, _c[allowHTML ? 'innerHTML' : 'innerText'] = label, _c));
+    Object.assign(div.dataset, {
+      item: '',
+      id: id,
+      value: value,
+      customProperties: customProperties
+    });
+    if (active) {
+      div.setAttribute('aria-selected', 'true');
+    }
+    if (disabled) {
+      div.setAttribute('aria-disabled', 'true');
+    }
+    if (isPlaceholder) {
+      div.classList.add(placeholder);
+    }
+    div.classList.add(highlighted ? highlightedState : itemSelectable);
+    if (removeItemButton) {
+      if (disabled) {
+        div.classList.remove(itemSelectable);
+      }
+      div.dataset.deletable = '';
+      /** @todo This MUST be localizable, not hardcoded! */
+      var REMOVE_ITEM_TEXT = 'Remove item';
+      var removeButton = Object.assign(document.createElement('button'), (_d = {
+        type: 'button',
+        className: button
+      }, _d[allowHTML ? 'innerHTML' : 'innerText'] = REMOVE_ITEM_TEXT, _d));
+      removeButton.setAttribute('aria-label', "".concat(REMOVE_ITEM_TEXT, ": '").concat(value, "'"));
+      removeButton.dataset.button = '';
+      div.appendChild(removeButton);
+    }
+    return div;
+  },
+  choiceList: function (_a, isSelectOneElement) {
+    var list = _a.classNames.list;
+    var div = Object.assign(document.createElement('div'), {
+      className: list
+    });
+    if (!isSelectOneElement) {
+      div.setAttribute('aria-multiselectable', 'true');
+    }
+    div.setAttribute('role', 'listbox');
+    return div;
+  },
+  choiceGroup: function (_a, _b) {
+    var _c;
+    var allowHTML = _a.allowHTML,
+      _d = _a.classNames,
+      group = _d.group,
+      groupHeading = _d.groupHeading,
+      itemDisabled = _d.itemDisabled;
+    var id = _b.id,
+      value = _b.value,
+      disabled = _b.disabled;
+    var div = Object.assign(document.createElement('div'), {
+      className: "".concat(group, " ").concat(disabled ? itemDisabled : '')
+    });
+    div.setAttribute('role', 'group');
+    Object.assign(div.dataset, {
+      group: '',
+      id: id,
+      value: value
+    });
+    if (disabled) {
+      div.setAttribute('aria-disabled', 'true');
+    }
+    div.appendChild(Object.assign(document.createElement('div'), (_c = {
+      className: groupHeading
+    }, _c[allowHTML ? 'innerHTML' : 'innerText'] = value, _c)));
+    return div;
+  },
+  choice: function (_a, _b, selectText) {
+    var _c;
+    var allowHTML = _a.allowHTML,
+      _d = _a.classNames,
+      item = _d.item,
+      itemChoice = _d.itemChoice,
+      itemSelectable = _d.itemSelectable,
+      selectedState = _d.selectedState,
+      itemDisabled = _d.itemDisabled,
+      placeholder = _d.placeholder;
+    var id = _b.id,
+      value = _b.value,
+      label = _b.label,
+      groupId = _b.groupId,
+      elementId = _b.elementId,
+      isDisabled = _b.disabled,
+      isSelected = _b.selected,
+      isPlaceholder = _b.placeholder;
+    var div = Object.assign(document.createElement('div'), (_c = {
+      id: elementId
+    }, _c[allowHTML ? 'innerHTML' : 'innerText'] = label, _c.className = "".concat(item, " ").concat(itemChoice), _c));
+    if (isSelected) {
+      div.classList.add(selectedState);
+    }
+    if (isPlaceholder) {
+      div.classList.add(placeholder);
+    }
+    div.setAttribute('role', groupId && groupId > 0 ? 'treeitem' : 'option');
+    Object.assign(div.dataset, {
+      choice: '',
+      id: id,
+      value: value,
+      selectText: selectText
+    });
+    if (isDisabled) {
+      div.classList.add(itemDisabled);
+      div.dataset.choiceDisabled = '';
+      div.setAttribute('aria-disabled', 'true');
+    } else {
+      div.classList.add(itemSelectable);
+      div.dataset.choiceSelectable = '';
+    }
+    return div;
+  },
+  input: function (_a, placeholderValue) {
+    var _b = _a.classNames,
+      input = _b.input,
+      inputCloned = _b.inputCloned;
+    var inp = Object.assign(document.createElement('input'), {
+      type: 'search',
+      name: 'search_terms',
+      className: "".concat(input, " ").concat(inputCloned),
+      autocomplete: 'off',
+      autocapitalize: 'off',
+      spellcheck: false
+    });
+    inp.setAttribute('role', 'textbox');
+    inp.setAttribute('aria-autocomplete', 'list');
+    inp.setAttribute('aria-label', placeholderValue);
+    return inp;
+  },
+  dropdown: function (_a) {
+    var _b = _a.classNames,
+      list = _b.list,
+      listDropdown = _b.listDropdown;
+    var div = document.createElement('div');
+    div.classList.add(list, listDropdown);
+    div.setAttribute('aria-expanded', 'false');
+    return div;
+  },
+  notice: function (_a, innerText, type) {
+    var _b;
+    var allowHTML = _a.allowHTML,
+      _c = _a.classNames,
+      item = _c.item,
+      itemChoice = _c.itemChoice,
+      noResults = _c.noResults,
+      noChoices = _c.noChoices;
+    if (type === void 0) {
+      type = '';
+    }
+    var classes = [item, itemChoice];
+    if (type === 'no-choices') {
+      classes.push(noChoices);
+    } else if (type === 'no-results') {
+      classes.push(noResults);
+    }
+    return Object.assign(document.createElement('div'), (_b = {}, _b[allowHTML ? 'innerHTML' : 'innerText'] = innerText, _b.className = classes.join(' '), _b));
+  },
+  option: function (_a) {
+    var label = _a.label,
+      value = _a.value,
+      customProperties = _a.customProperties,
+      active = _a.active,
+      disabled = _a.disabled;
+    var opt = new Option(label, value, false, active);
+    if (customProperties) {
+      opt.dataset.customProperties = "".concat(customProperties);
+    }
+    opt.disabled = !!disabled;
+    return opt;
+  }
+};
+exports["default"] = templates;
+
+/***/ }),
+
+/***/ 996:
+/***/ (function(module) {
+
+
+
+var isMergeableObject = function isMergeableObject(value) {
+	return isNonNullObject(value)
+		&& !isSpecial(value)
+};
+
+function isNonNullObject(value) {
+	return !!value && typeof value === 'object'
+}
+
+function isSpecial(value) {
+	var stringValue = Object.prototype.toString.call(value);
+
+	return stringValue === '[object RegExp]'
+		|| stringValue === '[object Date]'
+		|| isReactElement(value)
+}
+
+// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
+var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
+var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
+
+function isReactElement(value) {
+	return value.$$typeof === REACT_ELEMENT_TYPE
+}
+
+function emptyTarget(val) {
+	return Array.isArray(val) ? [] : {}
+}
+
+function cloneUnlessOtherwiseSpecified(value, options) {
+	return (options.clone !== false && options.isMergeableObject(value))
+		? deepmerge(emptyTarget(value), value, options)
+		: value
+}
+
+function defaultArrayMerge(target, source, options) {
+	return target.concat(source).map(function(element) {
+		return cloneUnlessOtherwiseSpecified(element, options)
+	})
+}
+
+function getMergeFunction(key, options) {
+	if (!options.customMerge) {
+		return deepmerge
+	}
+	var customMerge = options.customMerge(key);
+	return typeof customMerge === 'function' ? customMerge : deepmerge
+}
+
+function getEnumerableOwnPropertySymbols(target) {
+	return Object.getOwnPropertySymbols
+		? Object.getOwnPropertySymbols(target).filter(function(symbol) {
+			return target.propertyIsEnumerable(symbol)
+		})
+		: []
+}
+
+function getKeys(target) {
+	return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))
+}
+
+function propertyIsOnObject(object, property) {
+	try {
+		return property in object
+	} catch(_) {
+		return false
+	}
+}
+
+// Protects from prototype poisoning and unexpected merging up the prototype chain.
+function propertyIsUnsafe(target, key) {
+	return propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,
+		&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,
+			&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.
+}
+
+function mergeObject(target, source, options) {
+	var destination = {};
+	if (options.isMergeableObject(target)) {
+		getKeys(target).forEach(function(key) {
+			destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
+		});
+	}
+	getKeys(source).forEach(function(key) {
+		if (propertyIsUnsafe(target, key)) {
+			return
+		}
+
+		if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {
+			destination[key] = getMergeFunction(key, options)(target[key], source[key], options);
+		} else {
+			destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
+		}
+	});
+	return destination
+}
+
+function deepmerge(target, source, options) {
+	options = options || {};
+	options.arrayMerge = options.arrayMerge || defaultArrayMerge;
+	options.isMergeableObject = options.isMergeableObject || isMergeableObject;
+	// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()
+	// implementations can use it. The caller may not replace it.
+	options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;
+
+	var sourceIsArray = Array.isArray(source);
+	var targetIsArray = Array.isArray(target);
+	var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
+
+	if (!sourceAndTargetTypesMatch) {
+		return cloneUnlessOtherwiseSpecified(source, options)
+	} else if (sourceIsArray) {
+		return options.arrayMerge(target, source, options)
+	} else {
+		return mergeObject(target, source, options)
+	}
+}
+
+deepmerge.all = function deepmergeAll(array, options) {
+	if (!Array.isArray(array)) {
+		throw new Error('first argument should be an array')
+	}
+
+	return array.reduce(function(prev, next) {
+		return deepmerge(prev, next, options)
+	}, {})
+};
+
+var deepmerge_1 = deepmerge;
+
+module.exports = deepmerge_1;
+
+
+/***/ }),
+
+/***/ 221:
+/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
+
+__webpack_require__.r(__webpack_exports__);
+/* harmony export */ __webpack_require__.d(__webpack_exports__, {
+/* harmony export */   "default": function() { return /* binding */ Fuse; }
+/* harmony export */ });
+/**
+ * Fuse.js v6.6.2 - Lightweight fuzzy-search (http://fusejs.io)
+ *
+ * Copyright (c) 2022 Kiro Risk (http://kiro.me)
+ * All Rights Reserved. Apache Software License 2.0
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+function isArray(value) {
+  return !Array.isArray
+    ? getTag(value) === '[object Array]'
+    : Array.isArray(value)
+}
+
+// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js
+const INFINITY = 1 / 0;
+function baseToString(value) {
+  // Exit early for strings to avoid a performance hit in some environments.
+  if (typeof value == 'string') {
+    return value
+  }
+  let result = value + '';
+  return result == '0' && 1 / value == -INFINITY ? '-0' : result
+}
+
+function toString(value) {
+  return value == null ? '' : baseToString(value)
+}
+
+function isString(value) {
+  return typeof value === 'string'
+}
+
+function isNumber(value) {
+  return typeof value === 'number'
+}
+
+// Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js
+function isBoolean(value) {
+  return (
+    value === true ||
+    value === false ||
+    (isObjectLike(value) && getTag(value) == '[object Boolean]')
+  )
+}
+
+function isObject(value) {
+  return typeof value === 'object'
+}
+
+// Checks if `value` is object-like.
+function isObjectLike(value) {
+  return isObject(value) && value !== null
+}
+
+function isDefined(value) {
+  return value !== undefined && value !== null
+}
+
+function isBlank(value) {
+  return !value.trim().length
+}
+
+// Gets the `toStringTag` of `value`.
+// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js
+function getTag(value) {
+  return value == null
+    ? value === undefined
+      ? '[object Undefined]'
+      : '[object Null]'
+    : Object.prototype.toString.call(value)
+}
+
+const EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available';
+
+const INCORRECT_INDEX_TYPE = "Incorrect 'index' type";
+
+const LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = (key) =>
+  `Invalid value for key ${key}`;
+
+const PATTERN_LENGTH_TOO_LARGE = (max) =>
+  `Pattern length exceeds max of ${max}.`;
+
+const MISSING_KEY_PROPERTY = (name) => `Missing ${name} property in key`;
+
+const INVALID_KEY_WEIGHT_VALUE = (key) =>
+  `Property 'weight' in key '${key}' must be a positive integer`;
+
+const hasOwn = Object.prototype.hasOwnProperty;
+
+class KeyStore {
+  constructor(keys) {
+    this._keys = [];
+    this._keyMap = {};
+
+    let totalWeight = 0;
+
+    keys.forEach((key) => {
+      let obj = createKey(key);
+
+      totalWeight += obj.weight;
+
+      this._keys.push(obj);
+      this._keyMap[obj.id] = obj;
+
+      totalWeight += obj.weight;
+    });
+
+    // Normalize weights so that their sum is equal to 1
+    this._keys.forEach((key) => {
+      key.weight /= totalWeight;
+    });
+  }
+  get(keyId) {
+    return this._keyMap[keyId]
+  }
+  keys() {
+    return this._keys
+  }
+  toJSON() {
+    return JSON.stringify(this._keys)
+  }
+}
+
+function createKey(key) {
+  let path = null;
+  let id = null;
+  let src = null;
+  let weight = 1;
+  let getFn = null;
+
+  if (isString(key) || isArray(key)) {
+    src = key;
+    path = createKeyPath(key);
+    id = createKeyId(key);
+  } else {
+    if (!hasOwn.call(key, 'name')) {
+      throw new Error(MISSING_KEY_PROPERTY('name'))
+    }
+
+    const name = key.name;
+    src = name;
+
+    if (hasOwn.call(key, 'weight')) {
+      weight = key.weight;
+
+      if (weight <= 0) {
+        throw new Error(INVALID_KEY_WEIGHT_VALUE(name))
+      }
+    }
+
+    path = createKeyPath(name);
+    id = createKeyId(name);
+    getFn = key.getFn;
+  }
+
+  return { path, id, weight, src, getFn }
+}
+
+function createKeyPath(key) {
+  return isArray(key) ? key : key.split('.')
+}
+
+function createKeyId(key) {
+  return isArray(key) ? key.join('.') : key
+}
+
+function get(obj, path) {
+  let list = [];
+  let arr = false;
+
+  const deepGet = (obj, path, index) => {
+    if (!isDefined(obj)) {
+      return
+    }
+    if (!path[index]) {
+      // If there's no path left, we've arrived at the object we care about.
+      list.push(obj);
+    } else {
+      let key = path[index];
+
+      const value = obj[key];
+
+      if (!isDefined(value)) {
+        return
+      }
+
+      // If we're at the last value in the path, and if it's a string/number/bool,
+      // add it to the list
+      if (
+        index === path.length - 1 &&
+        (isString(value) || isNumber(value) || isBoolean(value))
+      ) {
+        list.push(toString(value));
+      } else if (isArray(value)) {
+        arr = true;
+        // Search each item in the array.
+        for (let i = 0, len = value.length; i < len; i += 1) {
+          deepGet(value[i], path, index + 1);
+        }
+      } else if (path.length) {
+        // An object. Recurse further.
+        deepGet(value, path, index + 1);
+      }
+    }
+  };
+
+  // Backwards compatibility (since path used to be a string)
+  deepGet(obj, isString(path) ? path.split('.') : path, 0);
+
+  return arr ? list : list[0]
+}
+
+const MatchOptions = {
+  // Whether the matches should be included in the result set. When `true`, each record in the result
+  // set will include the indices of the matched characters.
+  // These can consequently be used for highlighting purposes.
+  includeMatches: false,
+  // When `true`, the matching function will continue to the end of a search pattern even if
+  // a perfect match has already been located in the string.
+  findAllMatches: false,
+  // Minimum number of characters that must be matched before a result is considered a match
+  minMatchCharLength: 1
+};
+
+const BasicOptions = {
+  // When `true`, the algorithm continues searching to the end of the input even if a perfect
+  // match is found before the end of the same input.
+  isCaseSensitive: false,
+  // When true, the matching function will continue to the end of a search pattern even if
+  includeScore: false,
+  // List of properties that will be searched. This also supports nested properties.
+  keys: [],
+  // Whether to sort the result list, by score
+  shouldSort: true,
+  // Default sort function: sort by ascending score, ascending index
+  sortFn: (a, b) =>
+    a.score === b.score ? (a.idx < b.idx ? -1 : 1) : a.score < b.score ? -1 : 1
+};
+
+const FuzzyOptions = {
+  // Approximately where in the text is the pattern expected to be found?
+  location: 0,
+  // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match
+  // (of both letters and location), a threshold of '1.0' would match anything.
+  threshold: 0.6,
+  // Determines how close the match must be to the fuzzy location (specified above).
+  // An exact letter match which is 'distance' characters away from the fuzzy location
+  // would score as a complete mismatch. A distance of '0' requires the match be at
+  // the exact location specified, a threshold of '1000' would require a perfect match
+  // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold.
+  distance: 100
+};
+
+const AdvancedOptions = {
+  // When `true`, it enables the use of unix-like search commands
+  useExtendedSearch: false,
+  // The get function to use when fetching an object's properties.
+  // The default will search nested paths *ie foo.bar.baz*
+  getFn: get,
+  // When `true`, search will ignore `location` and `distance`, so it won't matter
+  // where in the string the pattern appears.
+  // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score
+  ignoreLocation: false,
+  // When `true`, the calculation for the relevance score (used for sorting) will
+  // ignore the field-length norm.
+  // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm
+  ignoreFieldNorm: false,
+  // The weight to determine how much field length norm effects scoring.
+  fieldNormWeight: 1
+};
+
+var Config = {
+  ...BasicOptions,
+  ...MatchOptions,
+  ...FuzzyOptions,
+  ...AdvancedOptions
+};
+
+const SPACE = /[^ ]+/g;
+
+// Field-length norm: the shorter the field, the higher the weight.
+// Set to 3 decimals to reduce index size.
+function norm(weight = 1, mantissa = 3) {
+  const cache = new Map();
+  const m = Math.pow(10, mantissa);
+
+  return {
+    get(value) {
+      const numTokens = value.match(SPACE).length;
+
+      if (cache.has(numTokens)) {
+        return cache.get(numTokens)
+      }
+
+      // Default function is 1/sqrt(x), weight makes that variable
+      const norm = 1 / Math.pow(numTokens, 0.5 * weight);
+
+      // In place of `toFixed(mantissa)`, for faster computation
+      const n = parseFloat(Math.round(norm * m) / m);
+
+      cache.set(numTokens, n);
+
+      return n
+    },
+    clear() {
+      cache.clear();
+    }
+  }
+}
+
+class FuseIndex {
+  constructor({
+    getFn = Config.getFn,
+    fieldNormWeight = Config.fieldNormWeight
+  } = {}) {
+    this.norm = norm(fieldNormWeight, 3);
+    this.getFn = getFn;
+    this.isCreated = false;
+
+    this.setIndexRecords();
+  }
+  setSources(docs = []) {
+    this.docs = docs;
+  }
+  setIndexRecords(records = []) {
+    this.records = records;
+  }
+  setKeys(keys = []) {
+    this.keys = keys;
+    this._keysMap = {};
+    keys.forEach((key, idx) => {
+      this._keysMap[key.id] = idx;
+    });
+  }
+  create() {
+    if (this.isCreated || !this.docs.length) {
+      return
+    }
+
+    this.isCreated = true;
+
+    // List is Array<String>
+    if (isString(this.docs[0])) {
+      this.docs.forEach((doc, docIndex) => {
+        this._addString(doc, docIndex);
+      });
+    } else {
+      // List is Array<Object>
+      this.docs.forEach((doc, docIndex) => {
+        this._addObject(doc, docIndex);
+      });
+    }
+
+    this.norm.clear();
+  }
+  // Adds a doc to the end of the index
+  add(doc) {
+    const idx = this.size();
+
+    if (isString(doc)) {
+      this._addString(doc, idx);
+    } else {
+      this._addObject(doc, idx);
+    }
+  }
+  // Removes the doc at the specified index of the index
+  removeAt(idx) {
+    this.records.splice(idx, 1);
+
+    // Change ref index of every subsquent doc
+    for (let i = idx, len = this.size(); i < len; i += 1) {
+      this.records[i].i -= 1;
+    }
+  }
+  getValueForItemAtKeyId(item, keyId) {
+    return item[this._keysMap[keyId]]
+  }
+  size() {
+    return this.records.length
+  }
+  _addString(doc, docIndex) {
+    if (!isDefined(doc) || isBlank(doc)) {
+      return
+    }
+
+    let record = {
+      v: doc,
+      i: docIndex,
+      n: this.norm.get(doc)
+    };
+
+    this.records.push(record);
+  }
+  _addObject(doc, docIndex) {
+    let record = { i: docIndex, $: {} };
+
+    // Iterate over every key (i.e, path), and fetch the value at that key
+    this.keys.forEach((key, keyIndex) => {
+      let value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
+
+      if (!isDefined(value)) {
+        return
+      }
+
+      if (isArray(value)) {
+        let subRecords = [];
+        const stack = [{ nestedArrIndex: -1, value }];
+
+        while (stack.length) {
+          const { nestedArrIndex, value } = stack.pop();
+
+          if (!isDefined(value)) {
+            continue
+          }
+
+          if (isString(value) && !isBlank(value)) {
+            let subRecord = {
+              v: value,
+              i: nestedArrIndex,
+              n: this.norm.get(value)
+            };
+
+            subRecords.push(subRecord);
+          } else if (isArray(value)) {
+            value.forEach((item, k) => {
+              stack.push({
+                nestedArrIndex: k,
+                value: item
+              });
+            });
+          } else ;
+        }
+        record.$[keyIndex] = subRecords;
+      } else if (isString(value) && !isBlank(value)) {
+        let subRecord = {
+          v: value,
+          n: this.norm.get(value)
+        };
+
+        record.$[keyIndex] = subRecord;
+      }
+    });
+
+    this.records.push(record);
+  }
+  toJSON() {
+    return {
+      keys: this.keys,
+      records: this.records
+    }
+  }
+}
+
+function createIndex(
+  keys,
+  docs,
+  { getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}
+) {
+  const myIndex = new FuseIndex({ getFn, fieldNormWeight });
+  myIndex.setKeys(keys.map(createKey));
+  myIndex.setSources(docs);
+  myIndex.create();
+  return myIndex
+}
+
+function parseIndex(
+  data,
+  { getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}
+) {
+  const { keys, records } = data;
+  const myIndex = new FuseIndex({ getFn, fieldNormWeight });
+  myIndex.setKeys(keys);
+  myIndex.setIndexRecords(records);
+  return myIndex
+}
+
+function computeScore$1(
+  pattern,
+  {
+    errors = 0,
+    currentLocation = 0,
+    expectedLocation = 0,
+    distance = Config.distance,
+    ignoreLocation = Config.ignoreLocation
+  } = {}
+) {
+  const accuracy = errors / pattern.length;
+
+  if (ignoreLocation) {
+    return accuracy
+  }
+
+  const proximity = Math.abs(expectedLocation - currentLocation);
+
+  if (!distance) {
+    // Dodge divide by zero error.
+    return proximity ? 1.0 : accuracy
+  }
+
+  return accuracy + proximity / distance
+}
+
+function convertMaskToIndices(
+  matchmask = [],
+  minMatchCharLength = Config.minMatchCharLength
+) {
+  let indices = [];
+  let start = -1;
+  let end = -1;
+  let i = 0;
+
+  for (let len = matchmask.length; i < len; i += 1) {
+    let match = matchmask[i];
+    if (match && start === -1) {
+      start = i;
+    } else if (!match && start !== -1) {
+      end = i - 1;
+      if (end - start + 1 >= minMatchCharLength) {
+        indices.push([start, end]);
+      }
+      start = -1;
+    }
+  }
+
+  // (i-1 - start) + 1 => i - start
+  if (matchmask[i - 1] && i - start >= minMatchCharLength) {
+    indices.push([start, i - 1]);
+  }
+
+  return indices
+}
+
+// Machine word size
+const MAX_BITS = 32;
+
+function search(
+  text,
+  pattern,
+  patternAlphabet,
+  {
+    location = Config.location,
+    distance = Config.distance,
+    threshold = Config.threshold,
+    findAllMatches = Config.findAllMatches,
+    minMatchCharLength = Config.minMatchCharLength,
+    includeMatches = Config.includeMatches,
+    ignoreLocation = Config.ignoreLocation
+  } = {}
+) {
+  if (pattern.length > MAX_BITS) {
+    throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS))
+  }
+
+  const patternLen = pattern.length;
+  // Set starting location at beginning text and initialize the alphabet.
+  const textLen = text.length;
+  // Handle the case when location > text.length
+  const expectedLocation = Math.max(0, Math.min(location, textLen));
+  // Highest score beyond which we give up.
+  let currentThreshold = threshold;
+  // Is there a nearby exact match? (speedup)
+  let bestLocation = expectedLocation;
+
+  // Performance: only computer matches when the minMatchCharLength > 1
+  // OR if `includeMatches` is true.
+  const computeMatches = minMatchCharLength > 1 || includeMatches;
+  // A mask of the matches, used for building the indices
+  const matchMask = computeMatches ? Array(textLen) : [];
+
+  let index;
+
+  // Get all exact matches, here for speed up
+  while ((index = text.indexOf(pattern, bestLocation)) > -1) {
+    let score = computeScore$1(pattern, {
+      currentLocation: index,
+      expectedLocation,
+      distance,
+      ignoreLocation
+    });
+
+    currentThreshold = Math.min(score, currentThreshold);
+    bestLocation = index + patternLen;
+
+    if (computeMatches) {
+      let i = 0;
+      while (i < patternLen) {
+        matchMask[index + i] = 1;
+        i += 1;
+      }
+    }
+  }
+
+  // Reset the best location
+  bestLocation = -1;
+
+  let lastBitArr = [];
+  let finalScore = 1;
+  let binMax = patternLen + textLen;
+
+  const mask = 1 << (patternLen - 1);
+
+  for (let i = 0; i < patternLen; i += 1) {
+    // Scan for the best match; each iteration allows for one more error.
+    // Run a binary search to determine how far from the match location we can stray
+    // at this error level.
+    let binMin = 0;
+    let binMid = binMax;
+
+    while (binMin < binMid) {
+      const score = computeScore$1(pattern, {
+        errors: i,
+        currentLocation: expectedLocation + binMid,
+        expectedLocation,
+        distance,
+        ignoreLocation
+      });
+
+      if (score <= currentThreshold) {
+        binMin = binMid;
+      } else {
+        binMax = binMid;
+      }
+
+      binMid = Math.floor((binMax - binMin) / 2 + binMin);
+    }
+
+    // Use the result from this iteration as the maximum for the next.
+    binMax = binMid;
+
+    let start = Math.max(1, expectedLocation - binMid + 1);
+    let finish = findAllMatches
+      ? textLen
+      : Math.min(expectedLocation + binMid, textLen) + patternLen;
+
+    // Initialize the bit array
+    let bitArr = Array(finish + 2);
+
+    bitArr[finish + 1] = (1 << i) - 1;
+
+    for (let j = finish; j >= start; j -= 1) {
+      let currentLocation = j - 1;
+      let charMatch = patternAlphabet[text.charAt(currentLocation)];
+
+      if (computeMatches) {
+        // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`)
+        matchMask[currentLocation] = +!!charMatch;
+      }
+
+      // First pass: exact match
+      bitArr[j] = ((bitArr[j + 1] << 1) | 1) & charMatch;
+
+      // Subsequent passes: fuzzy match
+      if (i) {
+        bitArr[j] |=
+          ((lastBitArr[j + 1] | lastBitArr[j]) << 1) | 1 | lastBitArr[j + 1];
+      }
+
+      if (bitArr[j] & mask) {
+        finalScore = computeScore$1(pattern, {
+          errors: i,
+          currentLocation,
+          expectedLocation,
+          distance,
+          ignoreLocation
+        });
+
+        // This match will almost certainly be better than any existing match.
+        // But check anyway.
+        if (finalScore <= currentThreshold) {
+          // Indeed it is
+          currentThreshold = finalScore;
+          bestLocation = currentLocation;
+
+          // Already passed `loc`, downhill from here on in.
+          if (bestLocation <= expectedLocation) {
+            break
+          }
+
+          // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`.
+          start = Math.max(1, 2 * expectedLocation - bestLocation);
+        }
+      }
+    }
+
+    // No hope for a (better) match at greater error levels.
+    const score = computeScore$1(pattern, {
+      errors: i + 1,
+      currentLocation: expectedLocation,
+      expectedLocation,
+      distance,
+      ignoreLocation
+    });
+
+    if (score > currentThreshold) {
+      break
+    }
+
+    lastBitArr = bitArr;
+  }
+
+  const result = {
+    isMatch: bestLocation >= 0,
+    // Count exact matches (those with a score of 0) to be "almost" exact
+    score: Math.max(0.001, finalScore)
+  };
+
+  if (computeMatches) {
+    const indices = convertMaskToIndices(matchMask, minMatchCharLength);
+    if (!indices.length) {
+      result.isMatch = false;
+    } else if (includeMatches) {
+      result.indices = indices;
+    }
+  }
+
+  return result
+}
+
+function createPatternAlphabet(pattern) {
+  let mask = {};
+
+  for (let i = 0, len = pattern.length; i < len; i += 1) {
+    const char = pattern.charAt(i);
+    mask[char] = (mask[char] || 0) | (1 << (len - i - 1));
+  }
+
+  return mask
+}
+
+class BitapSearch {
+  constructor(
+    pattern,
+    {
+      location = Config.location,
+      threshold = Config.threshold,
+      distance = Config.distance,
+      includeMatches = Config.includeMatches,
+      findAllMatches = Config.findAllMatches,
+      minMatchCharLength = Config.minMatchCharLength,
+      isCaseSensitive = Config.isCaseSensitive,
+      ignoreLocation = Config.ignoreLocation
+    } = {}
+  ) {
+    this.options = {
+      location,
+      threshold,
+      distance,
+      includeMatches,
+      findAllMatches,
+      minMatchCharLength,
+      isCaseSensitive,
+      ignoreLocation
+    };
+
+    this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase();
+
+    this.chunks = [];
+
+    if (!this.pattern.length) {
+      return
+    }
+
+    const addChunk = (pattern, startIndex) => {
+      this.chunks.push({
+        pattern,
+        alphabet: createPatternAlphabet(pattern),
+        startIndex
+      });
+    };
+
+    const len = this.pattern.length;
+
+    if (len > MAX_BITS) {
+      let i = 0;
+      const remainder = len % MAX_BITS;
+      const end = len - remainder;
+
+      while (i < end) {
+        addChunk(this.pattern.substr(i, MAX_BITS), i);
+        i += MAX_BITS;
+      }
+
+      if (remainder) {
+        const startIndex = len - MAX_BITS;
+        addChunk(this.pattern.substr(startIndex), startIndex);
+      }
+    } else {
+      addChunk(this.pattern, 0);
+    }
+  }
+
+  searchIn(text) {
+    const { isCaseSensitive, includeMatches } = this.options;
+
+    if (!isCaseSensitive) {
+      text = text.toLowerCase();
+    }
+
+    // Exact match
+    if (this.pattern === text) {
+      let result = {
+        isMatch: true,
+        score: 0
+      };
+
+      if (includeMatches) {
+        result.indices = [[0, text.length - 1]];
+      }
+
+      return result
+    }
+
+    // Otherwise, use Bitap algorithm
+    const {
+      location,
+      distance,
+      threshold,
+      findAllMatches,
+      minMatchCharLength,
+      ignoreLocation
+    } = this.options;
+
+    let allIndices = [];
+    let totalScore = 0;
+    let hasMatches = false;
+
+    this.chunks.forEach(({ pattern, alphabet, startIndex }) => {
+      const { isMatch, score, indices } = search(text, pattern, alphabet, {
+        location: location + startIndex,
+        distance,
+        threshold,
+        findAllMatches,
+        minMatchCharLength,
+        includeMatches,
+        ignoreLocation
+      });
+
+      if (isMatch) {
+        hasMatches = true;
+      }
+
+      totalScore += score;
+
+      if (isMatch && indices) {
+        allIndices = [...allIndices, ...indices];
+      }
+    });
+
+    let result = {
+      isMatch: hasMatches,
+      score: hasMatches ? totalScore / this.chunks.length : 1
+    };
+
+    if (hasMatches && includeMatches) {
+      result.indices = allIndices;
+    }
+
+    return result
+  }
+}
+
+class BaseMatch {
+  constructor(pattern) {
+    this.pattern = pattern;
+  }
+  static isMultiMatch(pattern) {
+    return getMatch(pattern, this.multiRegex)
+  }
+  static isSingleMatch(pattern) {
+    return getMatch(pattern, this.singleRegex)
+  }
+  search(/*text*/) {}
+}
+
+function getMatch(pattern, exp) {
+  const matches = pattern.match(exp);
+  return matches ? matches[1] : null
+}
+
+// Token: 'file
+
+class ExactMatch extends BaseMatch {
+  constructor(pattern) {
+    super(pattern);
+  }
+  static get type() {
+    return 'exact'
+  }
+  static get multiRegex() {
+    return /^="(.*)"$/
+  }
+  static get singleRegex() {
+    return /^=(.*)$/
+  }
+  search(text) {
+    const isMatch = text === this.pattern;
+
+    return {
+      isMatch,
+      score: isMatch ? 0 : 1,
+      indices: [0, this.pattern.length - 1]
+    }
+  }
+}
+
+// Token: !fire
+
+class InverseExactMatch extends BaseMatch {
+  constructor(pattern) {
+    super(pattern);
+  }
+  static get type() {
+    return 'inverse-exact'
+  }
+  static get multiRegex() {
+    return /^!"(.*)"$/
+  }
+  static get singleRegex() {
+    return /^!(.*)$/
+  }
+  search(text) {
+    const index = text.indexOf(this.pattern);
+    const isMatch = index === -1;
+
+    return {
+      isMatch,
+      score: isMatch ? 0 : 1,
+      indices: [0, text.length - 1]
+    }
+  }
+}
+
+// Token: ^file
+
+class PrefixExactMatch extends BaseMatch {
+  constructor(pattern) {
+    super(pattern);
+  }
+  static get type() {
+    return 'prefix-exact'
+  }
+  static get multiRegex() {
+    return /^\^"(.*)"$/
+  }
+  static get singleRegex() {
+    return /^\^(.*)$/
+  }
+  search(text) {
+    const isMatch = text.startsWith(this.pattern);
+
+    return {
+      isMatch,
+      score: isMatch ? 0 : 1,
+      indices: [0, this.pattern.length - 1]
+    }
+  }
+}
+
+// Token: !^fire
+
+class InversePrefixExactMatch extends BaseMatch {
+  constructor(pattern) {
+    super(pattern);
+  }
+  static get type() {
+    return 'inverse-prefix-exact'
+  }
+  static get multiRegex() {
+    return /^!\^"(.*)"$/
+  }
+  static get singleRegex() {
+    return /^!\^(.*)$/
+  }
+  search(text) {
+    const isMatch = !text.startsWith(this.pattern);
+
+    return {
+      isMatch,
+      score: isMatch ? 0 : 1,
+      indices: [0, text.length - 1]
+    }
+  }
+}
+
+// Token: .file$
+
+class SuffixExactMatch extends BaseMatch {
+  constructor(pattern) {
+    super(pattern);
+  }
+  static get type() {
+    return 'suffix-exact'
+  }
+  static get multiRegex() {
+    return /^"(.*)"\$$/
+  }
+  static get singleRegex() {
+    return /^(.*)\$$/
+  }
+  search(text) {
+    const isMatch = text.endsWith(this.pattern);
+
+    return {
+      isMatch,
+      score: isMatch ? 0 : 1,
+      indices: [text.length - this.pattern.length, text.length - 1]
+    }
+  }
+}
+
+// Token: !.file$
+
+class InverseSuffixExactMatch extends BaseMatch {
+  constructor(pattern) {
+    super(pattern);
+  }
+  static get type() {
+    return 'inverse-suffix-exact'
+  }
+  static get multiRegex() {
+    return /^!"(.*)"\$$/
+  }
+  static get singleRegex() {
+    return /^!(.*)\$$/
+  }
+  search(text) {
+    const isMatch = !text.endsWith(this.pattern);
+    return {
+      isMatch,
+      score: isMatch ? 0 : 1,
+      indices: [0, text.length - 1]
+    }
+  }
+}
+
+class FuzzyMatch extends BaseMatch {
+  constructor(
+    pattern,
+    {
+      location = Config.location,
+      threshold = Config.threshold,
+      distance = Config.distance,
+      includeMatches = Config.includeMatches,
+      findAllMatches = Config.findAllMatches,
+      minMatchCharLength = Config.minMatchCharLength,
+      isCaseSensitive = Config.isCaseSensitive,
+      ignoreLocation = Config.ignoreLocation
+    } = {}
+  ) {
+    super(pattern);
+    this._bitapSearch = new BitapSearch(pattern, {
+      location,
+      threshold,
+      distance,
+      includeMatches,
+      findAllMatches,
+      minMatchCharLength,
+      isCaseSensitive,
+      ignoreLocation
+    });
+  }
+  static get type() {
+    return 'fuzzy'
+  }
+  static get multiRegex() {
+    return /^"(.*)"$/
+  }
+  static get singleRegex() {
+    return /^(.*)$/
+  }
+  search(text) {
+    return this._bitapSearch.searchIn(text)
+  }
+}
+
+// Token: 'file
+
+class IncludeMatch extends BaseMatch {
+  constructor(pattern) {
+    super(pattern);
+  }
+  static get type() {
+    return 'include'
+  }
+  static get multiRegex() {
+    return /^'"(.*)"$/
+  }
+  static get singleRegex() {
+    return /^'(.*)$/
+  }
+  search(text) {
+    let location = 0;
+    let index;
+
+    const indices = [];
+    const patternLen = this.pattern.length;
+
+    // Get all exact matches
+    while ((index = text.indexOf(this.pattern, location)) > -1) {
+      location = index + patternLen;
+      indices.push([index, location - 1]);
+    }
+
+    const isMatch = !!indices.length;
+
+    return {
+      isMatch,
+      score: isMatch ? 0 : 1,
+      indices
+    }
+  }
+}
+
+// ❗Order is important. DO NOT CHANGE.
+const searchers = [
+  ExactMatch,
+  IncludeMatch,
+  PrefixExactMatch,
+  InversePrefixExactMatch,
+  InverseSuffixExactMatch,
+  SuffixExactMatch,
+  InverseExactMatch,
+  FuzzyMatch
+];
+
+const searchersLen = searchers.length;
+
+// Regex to split by spaces, but keep anything in quotes together
+const SPACE_RE = / +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/;
+const OR_TOKEN = '|';
+
+// Return a 2D array representation of the query, for simpler parsing.
+// Example:
+// "^core go$ | rb$ | py$ xy$" => [["^core", "go$"], ["rb$"], ["py$", "xy$"]]
+function parseQuery(pattern, options = {}) {
+  return pattern.split(OR_TOKEN).map((item) => {
+    let query = item
+      .trim()
+      .split(SPACE_RE)
+      .filter((item) => item && !!item.trim());
+
+    let results = [];
+    for (let i = 0, len = query.length; i < len; i += 1) {
+      const queryItem = query[i];
+
+      // 1. Handle multiple query match (i.e, once that are quoted, like `"hello world"`)
+      let found = false;
+      let idx = -1;
+      while (!found && ++idx < searchersLen) {
+        const searcher = searchers[idx];
+        let token = searcher.isMultiMatch(queryItem);
+        if (token) {
+          results.push(new searcher(token, options));
+          found = true;
+        }
+      }
+
+      if (found) {
+        continue
+      }
+
+      // 2. Handle single query matches (i.e, once that are *not* quoted)
+      idx = -1;
+      while (++idx < searchersLen) {
+        const searcher = searchers[idx];
+        let token = searcher.isSingleMatch(queryItem);
+        if (token) {
+          results.push(new searcher(token, options));
+          break
+        }
+      }
+    }
+
+    return results
+  })
+}
+
+// These extended matchers can return an array of matches, as opposed
+// to a singl match
+const MultiMatchSet = new Set([FuzzyMatch.type, IncludeMatch.type]);
+
+/**
+ * Command-like searching
+ * ======================
+ *
+ * Given multiple search terms delimited by spaces.e.g. `^jscript .python$ ruby !java`,
+ * search in a given text.
+ *
+ * Search syntax:
+ *
+ * | Token       | Match type                 | Description                            |
+ * | ----------- | -------------------------- | -------------------------------------- |
+ * | `jscript`   | fuzzy-match                | Items that fuzzy match `jscript`       |
+ * | `=scheme`   | exact-match                | Items that are `scheme`                |
+ * | `'python`   | include-match              | Items that include `python`            |
+ * | `!ruby`     | inverse-exact-match        | Items that do not include `ruby`       |
+ * | `^java`     | prefix-exact-match         | Items that start with `java`           |
+ * | `!^earlang` | inverse-prefix-exact-match | Items that do not start with `earlang` |
+ * | `.js$`      | suffix-exact-match         | Items that end with `.js`              |
+ * | `!.go$`     | inverse-suffix-exact-match | Items that do not end with `.go`       |
+ *
+ * A single pipe character acts as an OR operator. For example, the following
+ * query matches entries that start with `core` and end with either`go`, `rb`,
+ * or`py`.
+ *
+ * ```
+ * ^core go$ | rb$ | py$
+ * ```
+ */
+class ExtendedSearch {
+  constructor(
+    pattern,
+    {
+      isCaseSensitive = Config.isCaseSensitive,
+      includeMatches = Config.includeMatches,
+      minMatchCharLength = Config.minMatchCharLength,
+      ignoreLocation = Config.ignoreLocation,
+      findAllMatches = Config.findAllMatches,
+      location = Config.location,
+      threshold = Config.threshold,
+      distance = Config.distance
+    } = {}
+  ) {
+    this.query = null;
+    this.options = {
+      isCaseSensitive,
+      includeMatches,
+      minMatchCharLength,
+      findAllMatches,
+      ignoreLocation,
+      location,
+      threshold,
+      distance
+    };
+
+    this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase();
+    this.query = parseQuery(this.pattern, this.options);
+  }
+
+  static condition(_, options) {
+    return options.useExtendedSearch
+  }
+
+  searchIn(text) {
+    const query = this.query;
+
+    if (!query) {
+      return {
+        isMatch: false,
+        score: 1
+      }
+    }
+
+    const { includeMatches, isCaseSensitive } = this.options;
+
+    text = isCaseSensitive ? text : text.toLowerCase();
+
+    let numMatches = 0;
+    let allIndices = [];
+    let totalScore = 0;
+
+    // ORs
+    for (let i = 0, qLen = query.length; i < qLen; i += 1) {
+      const searchers = query[i];
+
+      // Reset indices
+      allIndices.length = 0;
+      numMatches = 0;
+
+      // ANDs
+      for (let j = 0, pLen = searchers.length; j < pLen; j += 1) {
+        const searcher = searchers[j];
+        const { isMatch, indices, score } = searcher.search(text);
+
+        if (isMatch) {
+          numMatches += 1;
+          totalScore += score;
+          if (includeMatches) {
+            const type = searcher.constructor.type;
+            if (MultiMatchSet.has(type)) {
+              allIndices = [...allIndices, ...indices];
+            } else {
+              allIndices.push(indices);
+            }
+          }
+        } else {
+          totalScore = 0;
+          numMatches = 0;
+          allIndices.length = 0;
+          break
+        }
+      }
+
+      // OR condition, so if TRUE, return
+      if (numMatches) {
+        let result = {
+          isMatch: true,
+          score: totalScore / numMatches
+        };
+
+        if (includeMatches) {
+          result.indices = allIndices;
+        }
+
+        return result
+      }
+    }
+
+    // Nothing was matched
+    return {
+      isMatch: false,
+      score: 1
+    }
+  }
+}
+
+const registeredSearchers = [];
+
+function register(...args) {
+  registeredSearchers.push(...args);
+}
+
+function createSearcher(pattern, options) {
+  for (let i = 0, len = registeredSearchers.length; i < len; i += 1) {
+    let searcherClass = registeredSearchers[i];
+    if (searcherClass.condition(pattern, options)) {
+      return new searcherClass(pattern, options)
+    }
+  }
+
+  return new BitapSearch(pattern, options)
+}
+
+const LogicalOperator = {
+  AND: '$and',
+  OR: '$or'
+};
+
+const KeyType = {
+  PATH: '$path',
+  PATTERN: '$val'
+};
+
+const isExpression = (query) =>
+  !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]);
+
+const isPath = (query) => !!query[KeyType.PATH];
+
+const isLeaf = (query) =>
+  !isArray(query) && isObject(query) && !isExpression(query);
+
+const convertToExplicit = (query) => ({
+  [LogicalOperator.AND]: Object.keys(query).map((key) => ({
+    [key]: query[key]
+  }))
+});
+
+// When `auto` is `true`, the parse function will infer and initialize and add
+// the appropriate `Searcher` instance
+function parse(query, options, { auto = true } = {}) {
+  const next = (query) => {
+    let keys = Object.keys(query);
+
+    const isQueryPath = isPath(query);
+
+    if (!isQueryPath && keys.length > 1 && !isExpression(query)) {
+      return next(convertToExplicit(query))
+    }
+
+    if (isLeaf(query)) {
+      const key = isQueryPath ? query[KeyType.PATH] : keys[0];
+
+      const pattern = isQueryPath ? query[KeyType.PATTERN] : query[key];
+
+      if (!isString(pattern)) {
+        throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key))
+      }
+
+      const obj = {
+        keyId: createKeyId(key),
+        pattern
+      };
+
+      if (auto) {
+        obj.searcher = createSearcher(pattern, options);
+      }
+
+      return obj
+    }
+
+    let node = {
+      children: [],
+      operator: keys[0]
+    };
+
+    keys.forEach((key) => {
+      const value = query[key];
+
+      if (isArray(value)) {
+        value.forEach((item) => {
+          node.children.push(next(item));
+        });
+      }
+    });
+
+    return node
+  };
+
+  if (!isExpression(query)) {
+    query = convertToExplicit(query);
+  }
+
+  return next(query)
+}
+
+// Practical scoring function
+function computeScore(
+  results,
+  { ignoreFieldNorm = Config.ignoreFieldNorm }
+) {
+  results.forEach((result) => {
+    let totalScore = 1;
+
+    result.matches.forEach(({ key, norm, score }) => {
+      const weight = key ? key.weight : null;
+
+      totalScore *= Math.pow(
+        score === 0 && weight ? Number.EPSILON : score,
+        (weight || 1) * (ignoreFieldNorm ? 1 : norm)
+      );
+    });
+
+    result.score = totalScore;
+  });
+}
+
+function transformMatches(result, data) {
+  const matches = result.matches;
+  data.matches = [];
+
+  if (!isDefined(matches)) {
+    return
+  }
+
+  matches.forEach((match) => {
+    if (!isDefined(match.indices) || !match.indices.length) {
+      return
+    }
+
+    const { indices, value } = match;
+
+    let obj = {
+      indices,
+      value
+    };
+
+    if (match.key) {
+      obj.key = match.key.src;
+    }
+
+    if (match.idx > -1) {
+      obj.refIndex = match.idx;
+    }
+
+    data.matches.push(obj);
+  });
+}
+
+function transformScore(result, data) {
+  data.score = result.score;
+}
+
+function format(
+  results,
+  docs,
+  {
+    includeMatches = Config.includeMatches,
+    includeScore = Config.includeScore
+  } = {}
+) {
+  const transformers = [];
+
+  if (includeMatches) transformers.push(transformMatches);
+  if (includeScore) transformers.push(transformScore);
+
+  return results.map((result) => {
+    const { idx } = result;
+
+    const data = {
+      item: docs[idx],
+      refIndex: idx
+    };
+
+    if (transformers.length) {
+      transformers.forEach((transformer) => {
+        transformer(result, data);
+      });
+    }
+
+    return data
+  })
+}
+
+class Fuse {
+  constructor(docs, options = {}, index) {
+    this.options = { ...Config, ...options };
+
+    if (
+      this.options.useExtendedSearch &&
+      !true
+    ) {}
+
+    this._keyStore = new KeyStore(this.options.keys);
+
+    this.setCollection(docs, index);
+  }
+
+  setCollection(docs, index) {
+    this._docs = docs;
+
+    if (index && !(index instanceof FuseIndex)) {
+      throw new Error(INCORRECT_INDEX_TYPE)
+    }
+
+    this._myIndex =
+      index ||
+      createIndex(this.options.keys, this._docs, {
+        getFn: this.options.getFn,
+        fieldNormWeight: this.options.fieldNormWeight
+      });
+  }
+
+  add(doc) {
+    if (!isDefined(doc)) {
+      return
+    }
+
+    this._docs.push(doc);
+    this._myIndex.add(doc);
+  }
+
+  remove(predicate = (/* doc, idx */) => false) {
+    const results = [];
+
+    for (let i = 0, len = this._docs.length; i < len; i += 1) {
+      const doc = this._docs[i];
+      if (predicate(doc, i)) {
+        this.removeAt(i);
+        i -= 1;
+        len -= 1;
+
+        results.push(doc);
+      }
+    }
+
+    return results
+  }
+
+  removeAt(idx) {
+    this._docs.splice(idx, 1);
+    this._myIndex.removeAt(idx);
+  }
+
+  getIndex() {
+    return this._myIndex
+  }
+
+  search(query, { limit = -1 } = {}) {
+    const {
+      includeMatches,
+      includeScore,
+      shouldSort,
+      sortFn,
+      ignoreFieldNorm
+    } = this.options;
+
+    let results = isString(query)
+      ? isString(this._docs[0])
+        ? this._searchStringList(query)
+        : this._searchObjectList(query)
+      : this._searchLogical(query);
+
+    computeScore(results, { ignoreFieldNorm });
+
+    if (shouldSort) {
+      results.sort(sortFn);
+    }
+
+    if (isNumber(limit) && limit > -1) {
+      results = results.slice(0, limit);
+    }
+
+    return format(results, this._docs, {
+      includeMatches,
+      includeScore
+    })
+  }
+
+  _searchStringList(query) {
+    const searcher = createSearcher(query, this.options);
+    const { records } = this._myIndex;
+    const results = [];
+
+    // Iterate over every string in the index
+    records.forEach(({ v: text, i: idx, n: norm }) => {
+      if (!isDefined(text)) {
+        return
+      }
+
+      const { isMatch, score, indices } = searcher.searchIn(text);
+
+      if (isMatch) {
+        results.push({
+          item: text,
+          idx,
+          matches: [{ score, value: text, norm, indices }]
+        });
+      }
+    });
+
+    return results
+  }
+
+  _searchLogical(query) {
+
+    const expression = parse(query, this.options);
+
+    const evaluate = (node, item, idx) => {
+      if (!node.children) {
+        const { keyId, searcher } = node;
+
+        const matches = this._findMatches({
+          key: this._keyStore.get(keyId),
+          value: this._myIndex.getValueForItemAtKeyId(item, keyId),
+          searcher
+        });
+
+        if (matches && matches.length) {
+          return [
+            {
+              idx,
+              item,
+              matches
+            }
+          ]
+        }
+
+        return []
+      }
+
+      const res = [];
+      for (let i = 0, len = node.children.length; i < len; i += 1) {
+        const child = node.children[i];
+        const result = evaluate(child, item, idx);
+        if (result.length) {
+          res.push(...result);
+        } else if (node.operator === LogicalOperator.AND) {
+          return []
+        }
+      }
+      return res
+    };
+
+    const records = this._myIndex.records;
+    const resultMap = {};
+    const results = [];
+
+    records.forEach(({ $: item, i: idx }) => {
+      if (isDefined(item)) {
+        let expResults = evaluate(expression, item, idx);
+
+        if (expResults.length) {
+          // Dedupe when adding
+          if (!resultMap[idx]) {
+            resultMap[idx] = { idx, item, matches: [] };
+            results.push(resultMap[idx]);
+          }
+          expResults.forEach(({ matches }) => {
+            resultMap[idx].matches.push(...matches);
+          });
+        }
+      }
+    });
+
+    return results
+  }
+
+  _searchObjectList(query) {
+    const searcher = createSearcher(query, this.options);
+    const { keys, records } = this._myIndex;
+    const results = [];
+
+    // List is Array<Object>
+    records.forEach(({ $: item, i: idx }) => {
+      if (!isDefined(item)) {
+        return
+      }
+
+      let matches = [];
+
+      // Iterate over every key (i.e, path), and fetch the value at that key
+      keys.forEach((key, keyIndex) => {
+        matches.push(
+          ...this._findMatches({
+            key,
+            value: item[keyIndex],
+            searcher
+          })
+        );
+      });
+
+      if (matches.length) {
+        results.push({
+          idx,
+          item,
+          matches
+        });
+      }
+    });
+
+    return results
+  }
+  _findMatches({ key, value, searcher }) {
+    if (!isDefined(value)) {
+      return []
+    }
+
+    let matches = [];
+
+    if (isArray(value)) {
+      value.forEach(({ v: text, i: idx, n: norm }) => {
+        if (!isDefined(text)) {
+          return
+        }
+
+        const { isMatch, score, indices } = searcher.searchIn(text);
+
+        if (isMatch) {
+          matches.push({
+            score,
+            key,
+            value: text,
+            idx,
+            norm,
+            indices
+          });
+        }
+      });
+    } else {
+      const { v: text, n: norm } = value;
+
+      const { isMatch, score, indices } = searcher.searchIn(text);
+
+      if (isMatch) {
+        matches.push({ score, key, value: text, norm, indices });
+      }
+    }
+
+    return matches
+  }
+}
+
+Fuse.version = '6.6.2';
+Fuse.createIndex = createIndex;
+Fuse.parseIndex = parseIndex;
+Fuse.config = Config;
+
+{
+  Fuse.parseQuery = parse;
+}
+
+{
+  register(ExtendedSearch);
+}
+
+
+
+
+/***/ }),
+
+/***/ 791:
+/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
+
+// ESM COMPAT FLAG
+__webpack_require__.r(__webpack_exports__);
+
+// EXPORTS
+__webpack_require__.d(__webpack_exports__, {
+  "__DO_NOT_USE__ActionTypes": function() { return /* binding */ ActionTypes; },
+  "applyMiddleware": function() { return /* binding */ applyMiddleware; },
+  "bindActionCreators": function() { return /* binding */ bindActionCreators; },
+  "combineReducers": function() { return /* binding */ combineReducers; },
+  "compose": function() { return /* binding */ compose; },
+  "createStore": function() { return /* binding */ createStore; },
+  "legacy_createStore": function() { return /* binding */ legacy_createStore; }
+});
+
+;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/typeof.js
+function _typeof(obj) {
+  "@babel/helpers - typeof";
+
+  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
+    return typeof obj;
+  } : function (obj) {
+    return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+  }, _typeof(obj);
+}
+;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/toPrimitive.js
+
+function _toPrimitive(input, hint) {
+  if (_typeof(input) !== "object" || input === null) return input;
+  var prim = input[Symbol.toPrimitive];
+  if (prim !== undefined) {
+    var res = prim.call(input, hint || "default");
+    if (_typeof(res) !== "object") return res;
+    throw new TypeError("@@toPrimitive must return a primitive value.");
+  }
+  return (hint === "string" ? String : Number)(input);
+}
+;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/toPropertyKey.js
+
+
+function _toPropertyKey(arg) {
+  var key = _toPrimitive(arg, "string");
+  return _typeof(key) === "symbol" ? key : String(key);
+}
+;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/defineProperty.js
+
+function _defineProperty(obj, key, value) {
+  key = _toPropertyKey(key);
+  if (key in obj) {
+    Object.defineProperty(obj, key, {
+      value: value,
+      enumerable: true,
+      configurable: true,
+      writable: true
+    });
+  } else {
+    obj[key] = value;
+  }
+  return obj;
+}
+;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectSpread2.js
+
+function ownKeys(object, enumerableOnly) {
+  var keys = Object.keys(object);
+  if (Object.getOwnPropertySymbols) {
+    var symbols = Object.getOwnPropertySymbols(object);
+    enumerableOnly && (symbols = symbols.filter(function (sym) {
+      return Object.getOwnPropertyDescriptor(object, sym).enumerable;
+    })), keys.push.apply(keys, symbols);
+  }
+  return keys;
+}
+function _objectSpread2(target) {
+  for (var i = 1; i < arguments.length; i++) {
+    var source = null != arguments[i] ? arguments[i] : {};
+    i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
+      _defineProperty(target, key, source[key]);
+    }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
+      Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
+    });
+  }
+  return target;
+}
+;// CONCATENATED MODULE: ./node_modules/redux/es/redux.js
+
+
+/**
+ * Adapted from React: https://github.com/facebook/react/blob/master/packages/shared/formatProdErrorMessage.js
+ *
+ * Do not require this module directly! Use normal throw error calls. These messages will be replaced with error codes
+ * during build.
+ * @param {number} code
+ */
+function formatProdErrorMessage(code) {
+  return "Minified Redux error #" + code + "; visit https://redux.js.org/Errors?code=" + code + " for the full message or " + 'use the non-minified dev environment for full errors. ';
+}
+
+// Inlined version of the `symbol-observable` polyfill
+var $$observable = (function () {
+  return typeof Symbol === 'function' && Symbol.observable || '@@observable';
+})();
+
+/**
+ * These are private action types reserved by Redux.
+ * For any unknown actions, you must return the current state.
+ * If the current state is undefined, you must return the initial state.
+ * Do not reference these action types directly in your code.
+ */
+var randomString = function randomString() {
+  return Math.random().toString(36).substring(7).split('').join('.');
+};
+
+var ActionTypes = {
+  INIT: "@@redux/INIT" + randomString(),
+  REPLACE: "@@redux/REPLACE" + randomString(),
+  PROBE_UNKNOWN_ACTION: function PROBE_UNKNOWN_ACTION() {
+    return "@@redux/PROBE_UNKNOWN_ACTION" + randomString();
+  }
+};
+
+/**
+ * @param {any} obj The object to inspect.
+ * @returns {boolean} True if the argument appears to be a plain object.
+ */
+function isPlainObject(obj) {
+  if (typeof obj !== 'object' || obj === null) return false;
+  var proto = obj;
+
+  while (Object.getPrototypeOf(proto) !== null) {
+    proto = Object.getPrototypeOf(proto);
+  }
+
+  return Object.getPrototypeOf(obj) === proto;
+}
+
+// Inlined / shortened version of `kindOf` from https://github.com/jonschlinkert/kind-of
+function miniKindOf(val) {
+  if (val === void 0) return 'undefined';
+  if (val === null) return 'null';
+  var type = typeof val;
+
+  switch (type) {
+    case 'boolean':
+    case 'string':
+    case 'number':
+    case 'symbol':
+    case 'function':
+      {
+        return type;
+      }
+  }
+
+  if (Array.isArray(val)) return 'array';
+  if (isDate(val)) return 'date';
+  if (isError(val)) return 'error';
+  var constructorName = ctorName(val);
+
+  switch (constructorName) {
+    case 'Symbol':
+    case 'Promise':
+    case 'WeakMap':
+    case 'WeakSet':
+    case 'Map':
+    case 'Set':
+      return constructorName;
+  } // other
+
+
+  return type.slice(8, -1).toLowerCase().replace(/\s/g, '');
+}
+
+function ctorName(val) {
+  return typeof val.constructor === 'function' ? val.constructor.name : null;
+}
+
+function isError(val) {
+  return val instanceof Error || typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number';
+}
+
+function isDate(val) {
+  if (val instanceof Date) return true;
+  return typeof val.toDateString === 'function' && typeof val.getDate === 'function' && typeof val.setDate === 'function';
+}
+
+function kindOf(val) {
+  var typeOfVal = typeof val;
+
+  if (false) {}
+
+  return typeOfVal;
+}
+
+/**
+ * @deprecated
+ *
+ * **We recommend using the `configureStore` method
+ * of the `@reduxjs/toolkit` package**, which replaces `createStore`.
+ *
+ * Redux Toolkit is our recommended approach for writing Redux logic today,
+ * including store setup, reducers, data fetching, and more.
+ *
+ * **For more details, please read this Redux docs page:**
+ * **https://redux.js.org/introduction/why-rtk-is-redux-today**
+ *
+ * `configureStore` from Redux Toolkit is an improved version of `createStore` that
+ * simplifies setup and helps avoid common bugs.
+ *
+ * You should not be using the `redux` core package by itself today, except for learning purposes.
+ * The `createStore` method from the core `redux` package will not be removed, but we encourage
+ * all users to migrate to using Redux Toolkit for all Redux code.
+ *
+ * If you want to use `createStore` without this visual deprecation warning, use
+ * the `legacy_createStore` import instead:
+ *
+ * `import { legacy_createStore as createStore} from 'redux'`
+ *
+ */
+
+function createStore(reducer, preloadedState, enhancer) {
+  var _ref2;
+
+  if (typeof preloadedState === 'function' && typeof enhancer === 'function' || typeof enhancer === 'function' && typeof arguments[3] === 'function') {
+    throw new Error( true ? formatProdErrorMessage(0) : 0);
+  }
+
+  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
+    enhancer = preloadedState;
+    preloadedState = undefined;
+  }
+
+  if (typeof enhancer !== 'undefined') {
+    if (typeof enhancer !== 'function') {
+      throw new Error( true ? formatProdErrorMessage(1) : 0);
+    }
+
+    return enhancer(createStore)(reducer, preloadedState);
+  }
+
+  if (typeof reducer !== 'function') {
+    throw new Error( true ? formatProdErrorMessage(2) : 0);
+  }
+
+  var currentReducer = reducer;
+  var currentState = preloadedState;
+  var currentListeners = [];
+  var nextListeners = currentListeners;
+  var isDispatching = false;
+  /**
+   * This makes a shallow copy of currentListeners so we can use
+   * nextListeners as a temporary list while dispatching.
+   *
+   * This prevents any bugs around consumers calling
+   * subscribe/unsubscribe in the middle of a dispatch.
+   */
+
+  function ensureCanMutateNextListeners() {
+    if (nextListeners === currentListeners) {
+      nextListeners = currentListeners.slice();
+    }
+  }
+  /**
+   * Reads the state tree managed by the store.
+   *
+   * @returns {any} The current state tree of your application.
+   */
+
+
+  function getState() {
+    if (isDispatching) {
+      throw new Error( true ? formatProdErrorMessage(3) : 0);
+    }
+
+    return currentState;
+  }
+  /**
+   * Adds a change listener. It will be called any time an action is dispatched,
+   * and some part of the state tree may potentially have changed. You may then
+   * call `getState()` to read the current state tree inside the callback.
+   *
+   * You may call `dispatch()` from a change listener, with the following
+   * caveats:
+   *
+   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
+   * If you subscribe or unsubscribe while the listeners are being invoked, this
+   * will not have any effect on the `dispatch()` that is currently in progress.
+   * However, the next `dispatch()` call, whether nested or not, will use a more
+   * recent snapshot of the subscription list.
+   *
+   * 2. The listener should not expect to see all state changes, as the state
+   * might have been updated multiple times during a nested `dispatch()` before
+   * the listener is called. It is, however, guaranteed that all subscribers
+   * registered before the `dispatch()` started will be called with the latest
+   * state by the time it exits.
+   *
+   * @param {Function} listener A callback to be invoked on every dispatch.
+   * @returns {Function} A function to remove this change listener.
+   */
+
+
+  function subscribe(listener) {
+    if (typeof listener !== 'function') {
+      throw new Error( true ? formatProdErrorMessage(4) : 0);
+    }
+
+    if (isDispatching) {
+      throw new Error( true ? formatProdErrorMessage(5) : 0);
+    }
+
+    var isSubscribed = true;
+    ensureCanMutateNextListeners();
+    nextListeners.push(listener);
+    return function unsubscribe() {
+      if (!isSubscribed) {
+        return;
+      }
+
+      if (isDispatching) {
+        throw new Error( true ? formatProdErrorMessage(6) : 0);
+      }
+
+      isSubscribed = false;
+      ensureCanMutateNextListeners();
+      var index = nextListeners.indexOf(listener);
+      nextListeners.splice(index, 1);
+      currentListeners = null;
+    };
+  }
+  /**
+   * Dispatches an action. It is the only way to trigger a state change.
+   *
+   * The `reducer` function, used to create the store, will be called with the
+   * current state tree and the given `action`. Its return value will
+   * be considered the **next** state of the tree, and the change listeners
+   * will be notified.
+   *
+   * The base implementation only supports plain object actions. If you want to
+   * dispatch a Promise, an Observable, a thunk, or something else, you need to
+   * wrap your store creating function into the corresponding middleware. For
+   * example, see the documentation for the `redux-thunk` package. Even the
+   * middleware will eventually dispatch plain object actions using this method.
+   *
+   * @param {Object} action A plain object representing “what changed”. It is
+   * a good idea to keep actions serializable so you can record and replay user
+   * sessions, or use the time travelling `redux-devtools`. An action must have
+   * a `type` property which may not be `undefined`. It is a good idea to use
+   * string constants for action types.
+   *
+   * @returns {Object} For convenience, the same action object you dispatched.
+   *
+   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
+   * return something else (for example, a Promise you can await).
+   */
+
+
+  function dispatch(action) {
+    if (!isPlainObject(action)) {
+      throw new Error( true ? formatProdErrorMessage(7) : 0);
+    }
+
+    if (typeof action.type === 'undefined') {
+      throw new Error( true ? formatProdErrorMessage(8) : 0);
+    }
+
+    if (isDispatching) {
+      throw new Error( true ? formatProdErrorMessage(9) : 0);
+    }
+
+    try {
+      isDispatching = true;
+      currentState = currentReducer(currentState, action);
+    } finally {
+      isDispatching = false;
+    }
+
+    var listeners = currentListeners = nextListeners;
+
+    for (var i = 0; i < listeners.length; i++) {
+      var listener = listeners[i];
+      listener();
+    }
+
+    return action;
+  }
+  /**
+   * Replaces the reducer currently used by the store to calculate the state.
+   *
+   * You might need this if your app implements code splitting and you want to
+   * load some of the reducers dynamically. You might also need this if you
+   * implement a hot reloading mechanism for Redux.
+   *
+   * @param {Function} nextReducer The reducer for the store to use instead.
+   * @returns {void}
+   */
+
+
+  function replaceReducer(nextReducer) {
+    if (typeof nextReducer !== 'function') {
+      throw new Error( true ? formatProdErrorMessage(10) : 0);
+    }
+
+    currentReducer = nextReducer; // This action has a similiar effect to ActionTypes.INIT.
+    // Any reducers that existed in both the new and old rootReducer
+    // will receive the previous state. This effectively populates
+    // the new state tree with any relevant data from the old one.
+
+    dispatch({
+      type: ActionTypes.REPLACE
+    });
+  }
+  /**
+   * Interoperability point for observable/reactive libraries.
+   * @returns {observable} A minimal observable of state changes.
+   * For more information, see the observable proposal:
+   * https://github.com/tc39/proposal-observable
+   */
+
+
+  function observable() {
+    var _ref;
+
+    var outerSubscribe = subscribe;
+    return _ref = {
+      /**
+       * The minimal observable subscription method.
+       * @param {Object} observer Any object that can be used as an observer.
+       * The observer object should have a `next` method.
+       * @returns {subscription} An object with an `unsubscribe` method that can
+       * be used to unsubscribe the observable from the store, and prevent further
+       * emission of values from the observable.
+       */
+      subscribe: function subscribe(observer) {
+        if (typeof observer !== 'object' || observer === null) {
+          throw new Error( true ? formatProdErrorMessage(11) : 0);
+        }
+
+        function observeState() {
+          if (observer.next) {
+            observer.next(getState());
+          }
+        }
+
+        observeState();
+        var unsubscribe = outerSubscribe(observeState);
+        return {
+          unsubscribe: unsubscribe
+        };
+      }
+    }, _ref[$$observable] = function () {
+      return this;
+    }, _ref;
+  } // When a store is created, an "INIT" action is dispatched so that every
+  // reducer returns their initial state. This effectively populates
+  // the initial state tree.
+
+
+  dispatch({
+    type: ActionTypes.INIT
+  });
+  return _ref2 = {
+    dispatch: dispatch,
+    subscribe: subscribe,
+    getState: getState,
+    replaceReducer: replaceReducer
+  }, _ref2[$$observable] = observable, _ref2;
+}
+/**
+ * Creates a Redux store that holds the state tree.
+ *
+ * **We recommend using `configureStore` from the
+ * `@reduxjs/toolkit` package**, which replaces `createStore`:
+ * **https://redux.js.org/introduction/why-rtk-is-redux-today**
+ *
+ * The only way to change the data in the store is to call `dispatch()` on it.
+ *
+ * There should only be a single store in your app. To specify how different
+ * parts of the state tree respond to actions, you may combine several reducers
+ * into a single reducer function by using `combineReducers`.
+ *
+ * @param {Function} reducer A function that returns the next state tree, given
+ * the current state tree and the action to handle.
+ *
+ * @param {any} [preloadedState] The initial state. You may optionally specify it
+ * to hydrate the state from the server in universal apps, or to restore a
+ * previously serialized user session.
+ * If you use `combineReducers` to produce the root reducer function, this must be
+ * an object with the same shape as `combineReducers` keys.
+ *
+ * @param {Function} [enhancer] The store enhancer. You may optionally specify it
+ * to enhance the store with third-party capabilities such as middleware,
+ * time travel, persistence, etc. The only store enhancer that ships with Redux
+ * is `applyMiddleware()`.
+ *
+ * @returns {Store} A Redux store that lets you read the state, dispatch actions
+ * and subscribe to changes.
+ */
+
+var legacy_createStore = createStore;
+
+/**
+ * Prints a warning in the console if it exists.
+ *
+ * @param {String} message The warning message.
+ * @returns {void}
+ */
+function warning(message) {
+  /* eslint-disable no-console */
+  if (typeof console !== 'undefined' && typeof console.error === 'function') {
+    console.error(message);
+  }
+  /* eslint-enable no-console */
+
+
+  try {
+    // This error was thrown as a convenience so that if you enable
+    // "break on all exceptions" in your console,
+    // it would pause the execution at this line.
+    throw new Error(message);
+  } catch (e) {} // eslint-disable-line no-empty
+
+}
+
+function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
+  var reducerKeys = Object.keys(reducers);
+  var argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer';
+
+  if (reducerKeys.length === 0) {
+    return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.';
+  }
+
+  if (!isPlainObject(inputState)) {
+    return "The " + argumentName + " has unexpected type of \"" + kindOf(inputState) + "\". Expected argument to be an object with the following " + ("keys: \"" + reducerKeys.join('", "') + "\"");
+  }
+
+  var unexpectedKeys = Object.keys(inputState).filter(function (key) {
+    return !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key];
+  });
+  unexpectedKeys.forEach(function (key) {
+    unexpectedKeyCache[key] = true;
+  });
+  if (action && action.type === ActionTypes.REPLACE) return;
+
+  if (unexpectedKeys.length > 0) {
+    return "Unexpected " + (unexpectedKeys.length > 1 ? 'keys' : 'key') + " " + ("\"" + unexpectedKeys.join('", "') + "\" found in " + argumentName + ". ") + "Expected to find one of the known reducer keys instead: " + ("\"" + reducerKeys.join('", "') + "\". Unexpected keys will be ignored.");
+  }
+}
+
+function assertReducerShape(reducers) {
+  Object.keys(reducers).forEach(function (key) {
+    var reducer = reducers[key];
+    var initialState = reducer(undefined, {
+      type: ActionTypes.INIT
+    });
+
+    if (typeof initialState === 'undefined') {
+      throw new Error( true ? formatProdErrorMessage(12) : 0);
+    }
+
+    if (typeof reducer(undefined, {
+      type: ActionTypes.PROBE_UNKNOWN_ACTION()
+    }) === 'undefined') {
+      throw new Error( true ? formatProdErrorMessage(13) : 0);
+    }
+  });
+}
+/**
+ * Turns an object whose values are different reducer functions, into a single
+ * reducer function. It will call every child reducer, and gather their results
+ * into a single state object, whose keys correspond to the keys of the passed
+ * reducer functions.
+ *
+ * @param {Object} reducers An object whose values correspond to different
+ * reducer functions that need to be combined into one. One handy way to obtain
+ * it is to use ES6 `import * as reducers` syntax. The reducers may never return
+ * undefined for any action. Instead, they should return their initial state
+ * if the state passed to them was undefined, and the current state for any
+ * unrecognized action.
+ *
+ * @returns {Function} A reducer function that invokes every reducer inside the
+ * passed object, and builds a state object with the same shape.
+ */
+
+
+function combineReducers(reducers) {
+  var reducerKeys = Object.keys(reducers);
+  var finalReducers = {};
+
+  for (var i = 0; i < reducerKeys.length; i++) {
+    var key = reducerKeys[i];
+
+    if (false) {}
+
+    if (typeof reducers[key] === 'function') {
+      finalReducers[key] = reducers[key];
+    }
+  }
+
+  var finalReducerKeys = Object.keys(finalReducers); // This is used to make sure we don't warn about the same
+  // keys multiple times.
+
+  var unexpectedKeyCache;
+
+  if (false) {}
+
+  var shapeAssertionError;
+
+  try {
+    assertReducerShape(finalReducers);
+  } catch (e) {
+    shapeAssertionError = e;
+  }
+
+  return function combination(state, action) {
+    if (state === void 0) {
+      state = {};
+    }
+
+    if (shapeAssertionError) {
+      throw shapeAssertionError;
+    }
+
+    if (false) { var warningMessage; }
+
+    var hasChanged = false;
+    var nextState = {};
+
+    for (var _i = 0; _i < finalReducerKeys.length; _i++) {
+      var _key = finalReducerKeys[_i];
+      var reducer = finalReducers[_key];
+      var previousStateForKey = state[_key];
+      var nextStateForKey = reducer(previousStateForKey, action);
+
+      if (typeof nextStateForKey === 'undefined') {
+        var actionType = action && action.type;
+        throw new Error( true ? formatProdErrorMessage(14) : 0);
+      }
+
+      nextState[_key] = nextStateForKey;
+      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
+    }
+
+    hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;
+    return hasChanged ? nextState : state;
+  };
+}
+
+function bindActionCreator(actionCreator, dispatch) {
+  return function () {
+    return dispatch(actionCreator.apply(this, arguments));
+  };
+}
+/**
+ * Turns an object whose values are action creators, into an object with the
+ * same keys, but with every function wrapped into a `dispatch` call so they
+ * may be invoked directly. This is just a convenience method, as you can call
+ * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
+ *
+ * For convenience, you can also pass an action creator as the first argument,
+ * and get a dispatch wrapped function in return.
+ *
+ * @param {Function|Object} actionCreators An object whose values are action
+ * creator functions. One handy way to obtain it is to use ES6 `import * as`
+ * syntax. You may also pass a single function.
+ *
+ * @param {Function} dispatch The `dispatch` function available on your Redux
+ * store.
+ *
+ * @returns {Function|Object} The object mimicking the original object, but with
+ * every action creator wrapped into the `dispatch` call. If you passed a
+ * function as `actionCreators`, the return value will also be a single
+ * function.
+ */
+
+
+function bindActionCreators(actionCreators, dispatch) {
+  if (typeof actionCreators === 'function') {
+    return bindActionCreator(actionCreators, dispatch);
+  }
+
+  if (typeof actionCreators !== 'object' || actionCreators === null) {
+    throw new Error( true ? formatProdErrorMessage(16) : 0);
+  }
+
+  var boundActionCreators = {};
+
+  for (var key in actionCreators) {
+    var actionCreator = actionCreators[key];
+
+    if (typeof actionCreator === 'function') {
+      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
+    }
+  }
+
+  return boundActionCreators;
+}
+
+/**
+ * Composes single-argument functions from right to left. The rightmost
+ * function can take multiple arguments as it provides the signature for
+ * the resulting composite function.
+ *
+ * @param {...Function} funcs The functions to compose.
+ * @returns {Function} A function obtained by composing the argument functions
+ * from right to left. For example, compose(f, g, h) is identical to doing
+ * (...args) => f(g(h(...args))).
+ */
+function compose() {
+  for (var _len = arguments.length, funcs = new Array(_len), _key = 0; _key < _len; _key++) {
+    funcs[_key] = arguments[_key];
+  }
+
+  if (funcs.length === 0) {
+    return function (arg) {
+      return arg;
+    };
+  }
+
+  if (funcs.length === 1) {
+    return funcs[0];
+  }
+
+  return funcs.reduce(function (a, b) {
+    return function () {
+      return a(b.apply(void 0, arguments));
+    };
+  });
+}
+
+/**
+ * Creates a store enhancer that applies middleware to the dispatch method
+ * of the Redux store. This is handy for a variety of tasks, such as expressing
+ * asynchronous actions in a concise manner, or logging every action payload.
+ *
+ * See `redux-thunk` package as an example of the Redux middleware.
+ *
+ * Because middleware is potentially asynchronous, this should be the first
+ * store enhancer in the composition chain.
+ *
+ * Note that each middleware will be given the `dispatch` and `getState` functions
+ * as named arguments.
+ *
+ * @param {...Function} middlewares The middleware chain to be applied.
+ * @returns {Function} A store enhancer applying the middleware.
+ */
+
+function applyMiddleware() {
+  for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) {
+    middlewares[_key] = arguments[_key];
+  }
+
+  return function (createStore) {
+    return function () {
+      var store = createStore.apply(void 0, arguments);
+
+      var _dispatch = function dispatch() {
+        throw new Error( true ? formatProdErrorMessage(15) : 0);
+      };
+
+      var middlewareAPI = {
+        getState: store.getState,
+        dispatch: function dispatch() {
+          return _dispatch.apply(void 0, arguments);
+        }
+      };
+      var chain = middlewares.map(function (middleware) {
+        return middleware(middlewareAPI);
+      });
+      _dispatch = compose.apply(void 0, chain)(store.dispatch);
+      return _objectSpread2(_objectSpread2({}, store), {}, {
+        dispatch: _dispatch
+      });
+    };
+  };
+}
+
+/*
+ * This is a dummy function to check if the function name has been altered by minification.
+ * If the function has been minified and NODE_ENV !== 'production', warn the user.
+ */
+
+function isCrushed() {}
+
+if (false) {}
+
+
+
+
+/***/ })
+
+/******/ 	});
+/************************************************************************/
+/******/ 	// The module cache
+/******/ 	var __webpack_module_cache__ = {};
+/******/ 	
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/ 		// Check if module is in cache
+/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
+/******/ 		if (cachedModule !== undefined) {
+/******/ 			return cachedModule.exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = __webpack_module_cache__[moduleId] = {
+/******/ 			// no module.id needed
+/******/ 			// no module.loaded needed
+/******/ 			exports: {}
+/******/ 		};
+/******/ 	
+/******/ 		// Execute the module function
+/******/ 		__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/ 	
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/ 	
+/************************************************************************/
+/******/ 	/* webpack/runtime/compat get default export */
+/******/ 	!function() {
+/******/ 		// getDefaultExport function for compatibility with non-harmony modules
+/******/ 		__webpack_require__.n = function(module) {
+/******/ 			var getter = module && module.__esModule ?
+/******/ 				function() { return module['default']; } :
+/******/ 				function() { return module; };
+/******/ 			__webpack_require__.d(getter, { a: getter });
+/******/ 			return getter;
+/******/ 		};
+/******/ 	}();
+/******/ 	
+/******/ 	/* webpack/runtime/define property getters */
+/******/ 	!function() {
+/******/ 		// define getter functions for harmony exports
+/******/ 		__webpack_require__.d = function(exports, definition) {
+/******/ 			for(var key in definition) {
+/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
+/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
+/******/ 				}
+/******/ 			}
+/******/ 		};
+/******/ 	}();
+/******/ 	
+/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
+/******/ 	!function() {
+/******/ 		__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
+/******/ 	}();
+/******/ 	
+/******/ 	/* webpack/runtime/make namespace object */
+/******/ 	!function() {
+/******/ 		// define __esModule on exports
+/******/ 		__webpack_require__.r = function(exports) {
+/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ 			}
+/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
+/******/ 		};
+/******/ 	}();
+/******/ 	
+/************************************************************************/
+var __webpack_exports__ = {};
+// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
+!function() {
+/* harmony import */ var _scripts_choices__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(373);
+/* harmony import */ var _scripts_choices__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_scripts_choices__WEBPACK_IMPORTED_MODULE_0__);
+/* harmony import */ var _scripts_interfaces__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(187);
+/* harmony import */ var _scripts_interfaces__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_scripts_interfaces__WEBPACK_IMPORTED_MODULE_1__);
+/* harmony import */ var _scripts_constants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(883);
+/* harmony import */ var _scripts_defaults__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(789);
+/* harmony import */ var _scripts_templates__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(686);
+
+
+
+
+
+
+
+/* harmony default export */ __webpack_exports__["default"] = ((_scripts_choices__WEBPACK_IMPORTED_MODULE_0___default()));
+
+}();
+__webpack_exports__ = __webpack_exports__["default"];
+/******/ 	return __webpack_exports__;
+/******/ })()
+;
+});
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/assets/scripts/choices.min.js b/my_flask_app/static/choices.js/public/assets/scripts/choices.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..af280949e76ffb1b653377565ea292b6bfe82e93
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/scripts/choices.min.js
@@ -0,0 +1,2 @@
+/*! For license information please see choices.min.js.LICENSE.txt */
+!function(){"use strict";var e={282:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.clearChoices=t.activateChoices=t.filterChoices=t.addChoice=void 0;var n=i(883);t.addChoice=function(e){var t=e.value,i=e.label,r=e.id,s=e.groupId,o=e.disabled,a=e.elementId,c=e.customProperties,l=e.placeholder,h=e.keyCode;return{type:n.ACTION_TYPES.ADD_CHOICE,value:t,label:i,id:r,groupId:s,disabled:o,elementId:a,customProperties:c,placeholder:l,keyCode:h}},t.filterChoices=function(e){return{type:n.ACTION_TYPES.FILTER_CHOICES,results:e}},t.activateChoices=function(e){return void 0===e&&(e=!0),{type:n.ACTION_TYPES.ACTIVATE_CHOICES,active:e}},t.clearChoices=function(){return{type:n.ACTION_TYPES.CLEAR_CHOICES}}},783:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.addGroup=void 0;var n=i(883);t.addGroup=function(e){var t=e.value,i=e.id,r=e.active,s=e.disabled;return{type:n.ACTION_TYPES.ADD_GROUP,value:t,id:i,active:r,disabled:s}}},464:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.highlightItem=t.removeItem=t.addItem=void 0;var n=i(883);t.addItem=function(e){var t=e.value,i=e.label,r=e.id,s=e.choiceId,o=e.groupId,a=e.customProperties,c=e.placeholder,l=e.keyCode;return{type:n.ACTION_TYPES.ADD_ITEM,value:t,label:i,id:r,choiceId:s,groupId:o,customProperties:a,placeholder:c,keyCode:l}},t.removeItem=function(e,t){return{type:n.ACTION_TYPES.REMOVE_ITEM,id:e,choiceId:t}},t.highlightItem=function(e,t){return{type:n.ACTION_TYPES.HIGHLIGHT_ITEM,id:e,highlighted:t}}},137:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.setIsLoading=t.resetTo=t.clearAll=void 0;var n=i(883);t.clearAll=function(){return{type:n.ACTION_TYPES.CLEAR_ALL}},t.resetTo=function(e){return{type:n.ACTION_TYPES.RESET_TO,state:e}},t.setIsLoading=function(e){return{type:n.ACTION_TYPES.SET_IS_LOADING,isLoading:e}}},373:function(e,t,i){var n=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r<s;r++)!n&&r in t||(n||(n=Array.prototype.slice.call(t,0,r)),n[r]=t[r]);return e.concat(n||Array.prototype.slice.call(t))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var s=r(i(996)),o=r(i(221)),a=i(282),c=i(783),l=i(464),h=i(137),u=i(520),d=i(883),p=i(789),f=i(799),m=i(655),v=r(i(744)),g=r(i(686)),_="-ms-scroll-limit"in document.documentElement.style&&"-ms-ime-align"in document.documentElement.style,y={},E=function(){function e(t,i){void 0===t&&(t="[data-choice]"),void 0===i&&(i={});var r=this;void 0===i.allowHTML&&console.warn("Deprecation warning: allowHTML will default to false in a future release. To render HTML in Choices, you will need to set it to true. Setting allowHTML will suppress this message."),this.config=s.default.all([p.DEFAULT_CONFIG,e.defaults.options,i],{arrayMerge:function(e,t){return n([],t,!0)}});var o=(0,f.diff)(this.config,p.DEFAULT_CONFIG);o.length&&console.warn("Unknown config option(s) passed",o.join(", "));var a="string"==typeof t?document.querySelector(t):t;if(!(a instanceof HTMLInputElement||a instanceof HTMLSelectElement))throw TypeError("Expected one of the following types text|select-one|select-multiple");if(this._isTextElement=a.type===d.TEXT_TYPE,this._isSelectOneElement=a.type===d.SELECT_ONE_TYPE,this._isSelectMultipleElement=a.type===d.SELECT_MULTIPLE_TYPE,this._isSelectElement=this._isSelectOneElement||this._isSelectMultipleElement,this.config.searchEnabled=this._isSelectMultipleElement||this.config.searchEnabled,["auto","always"].includes("".concat(this.config.renderSelectedChoices))||(this.config.renderSelectedChoices="auto"),i.addItemFilter&&"function"!=typeof i.addItemFilter){var c=i.addItemFilter instanceof RegExp?i.addItemFilter:new RegExp(i.addItemFilter);this.config.addItemFilter=c.test.bind(c)}if(this._isTextElement?this.passedElement=new u.WrappedInput({element:a,classNames:this.config.classNames,delimiter:this.config.delimiter}):this.passedElement=new u.WrappedSelect({element:a,classNames:this.config.classNames,template:function(e){return r._templates.option(e)}}),this.initialised=!1,this._store=new v.default,this._initialState=m.defaultState,this._currentState=m.defaultState,this._prevState=m.defaultState,this._currentValue="",this._canSearch=!!this.config.searchEnabled,this._isScrollingOnIe=!1,this._highlightPosition=0,this._wasTap=!0,this._placeholderValue=this._generatePlaceholderValue(),this._baseId=(0,f.generateId)(this.passedElement.element,"choices-"),this._direction=this.passedElement.dir,!this._direction){var l=window.getComputedStyle(this.passedElement.element).direction;l!==window.getComputedStyle(document.documentElement).direction&&(this._direction=l)}if(this._idNames={itemChoice:"item-choice"},this._isSelectElement&&(this._presetGroups=this.passedElement.optionGroups,this._presetOptions=this.passedElement.options),this._presetChoices=this.config.choices,this._presetItems=this.config.items,this.passedElement.value&&this._isTextElement){var h=this.passedElement.value.split(this.config.delimiter);this._presetItems=this._presetItems.concat(h)}if(this.passedElement.options&&this.passedElement.options.forEach((function(e){r._presetChoices.push({value:e.value,label:e.innerHTML,selected:!!e.selected,disabled:e.disabled||e.parentNode.disabled,placeholder:""===e.value||e.hasAttribute("placeholder"),customProperties:(0,f.parseCustomProperties)(e.dataset.customProperties)})})),this._render=this._render.bind(this),this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this),this._onKeyUp=this._onKeyUp.bind(this),this._onKeyDown=this._onKeyDown.bind(this),this._onClick=this._onClick.bind(this),this._onTouchMove=this._onTouchMove.bind(this),this._onTouchEnd=this._onTouchEnd.bind(this),this._onMouseDown=this._onMouseDown.bind(this),this._onMouseOver=this._onMouseOver.bind(this),this._onFormReset=this._onFormReset.bind(this),this._onSelectKey=this._onSelectKey.bind(this),this._onEnterKey=this._onEnterKey.bind(this),this._onEscapeKey=this._onEscapeKey.bind(this),this._onDirectionKey=this._onDirectionKey.bind(this),this._onDeleteKey=this._onDeleteKey.bind(this),this.passedElement.isActive)return this.config.silent||console.warn("Trying to initialise Choices on element already initialised",{element:t}),void(this.initialised=!0);this.init()}return Object.defineProperty(e,"defaults",{get:function(){return Object.preventExtensions({get options(){return y},get templates(){return g.default}})},enumerable:!1,configurable:!0}),e.prototype.init=function(){if(!this.initialised){this._createTemplates(),this._createElements(),this._createStructure(),this._store.subscribe(this._render),this._render(),this._addEventListeners(),(!this.config.addItems||this.passedElement.element.hasAttribute("disabled"))&&this.disable(),this.initialised=!0;var e=this.config.callbackOnInit;e&&"function"==typeof e&&e.call(this)}},e.prototype.destroy=function(){this.initialised&&(this._removeEventListeners(),this.passedElement.reveal(),this.containerOuter.unwrap(this.passedElement.element),this.clearStore(),this._isSelectElement&&(this.passedElement.options=this._presetOptions),this._templates=g.default,this.initialised=!1)},e.prototype.enable=function(){return this.passedElement.isDisabled&&this.passedElement.enable(),this.containerOuter.isDisabled&&(this._addEventListeners(),this.input.enable(),this.containerOuter.enable()),this},e.prototype.disable=function(){return this.passedElement.isDisabled||this.passedElement.disable(),this.containerOuter.isDisabled||(this._removeEventListeners(),this.input.disable(),this.containerOuter.disable()),this},e.prototype.highlightItem=function(e,t){if(void 0===t&&(t=!0),!e||!e.id)return this;var i=e.id,n=e.groupId,r=void 0===n?-1:n,s=e.value,o=void 0===s?"":s,a=e.label,c=void 0===a?"":a,h=r>=0?this._store.getGroupById(r):null;return this._store.dispatch((0,l.highlightItem)(i,!0)),t&&this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:i,value:o,label:c,groupValue:h&&h.value?h.value:null}),this},e.prototype.unhighlightItem=function(e){if(!e||!e.id)return this;var t=e.id,i=e.groupId,n=void 0===i?-1:i,r=e.value,s=void 0===r?"":r,o=e.label,a=void 0===o?"":o,c=n>=0?this._store.getGroupById(n):null;return this._store.dispatch((0,l.highlightItem)(t,!1)),this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:t,value:s,label:a,groupValue:c&&c.value?c.value:null}),this},e.prototype.highlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.highlightItem(t)})),this},e.prototype.unhighlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.unhighlightItem(t)})),this},e.prototype.removeActiveItemsByValue=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeActiveItems=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)})),this},e.prototype.showDropdown=function(e){var t=this;return this.dropdown.isActive||requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent(d.EVENTS.showDropdown,{})})),this},e.prototype.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent(d.EVENTS.hideDropdown,{})})),this):this},e.prototype.getValue=function(e){void 0===e&&(e=!1);var t=this._store.activeItems.reduce((function(t,i){var n=e?i.value:i;return t.push(n),t}),[]);return this._isSelectOneElement?t[0]:t},e.prototype.setValue=function(e){var t=this;return this.initialised?(e.forEach((function(e){return t._setChoiceOrItem(e)})),this):this},e.prototype.setChoiceByValue=function(e){var t=this;return!this.initialised||this._isTextElement||(Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)})),this},e.prototype.setChoices=function(e,t,i,n){var r=this;if(void 0===e&&(e=[]),void 0===t&&(t="value"),void 0===i&&(i="label"),void 0===n&&(n=!1),!this.initialised)throw new ReferenceError("setChoices was called on a non-initialized instance of Choices");if(!this._isSelectElement)throw new TypeError("setChoices can't be used with INPUT based Choices");if("string"!=typeof t||!t)throw new TypeError("value parameter must be a name of 'value' field in passed objects");if(n&&this.clearChoices(),"function"==typeof e){var s=e(this);if("function"==typeof Promise&&s instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return r._handleLoadingState(!0)})).then((function(){return s})).then((function(e){return r.setChoices(e,t,i,n)})).catch((function(e){r.config.silent||console.error(e)})).then((function(){return r._handleLoadingState(!1)})).then((function(){return r}));if(!Array.isArray(s))throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof s));return this.setChoices(s,t,i,!1)}if(!Array.isArray(e))throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");return this.containerOuter.removeLoadingState(),this._startLoading(),e.forEach((function(e){if(e.choices)r._addGroup({id:e.id?parseInt("".concat(e.id),10):null,group:e,valueKey:t,labelKey:i});else{var n=e;r._addChoice({value:n[t],label:n[i],isSelected:!!n.selected,isDisabled:!!n.disabled,placeholder:!!n.placeholder,customProperties:n.customProperties})}})),this._stopLoading(),this},e.prototype.clearChoices=function(){return this._store.dispatch((0,a.clearChoices)()),this},e.prototype.clearStore=function(){return this._store.dispatch((0,h.clearAll)()),this},e.prototype.clearInput=function(){var e=!this._isSelectOneElement;return this.input.clear(e),!this._isTextElement&&this._canSearch&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))),this},e.prototype._render=function(){if(!this._store.isLoading()){this._currentState=this._store.state;var e=this._currentState.choices!==this._prevState.choices||this._currentState.groups!==this._prevState.groups||this._currentState.items!==this._prevState.items,t=this._isSelectElement,i=this._currentState.items!==this._prevState.items;e&&(t&&this._renderChoices(),i&&this._renderItems(),this._prevState=this._currentState)}},e.prototype._renderChoices=function(){var e=this,t=this._store,i=t.activeGroups,n=t.activeChoices,r=document.createDocumentFragment();if(this.choiceList.clear(),this.config.resetScrollPosition&&requestAnimationFrame((function(){return e.choiceList.scrollToTop()})),i.length>=1&&!this._isSearching){var s=n.filter((function(e){return!0===e.placeholder&&-1===e.groupId}));s.length>=1&&(r=this._createChoicesFragment(s,r)),r=this._createGroupsFragment(i,n,r)}else n.length>=1&&(r=this._createChoicesFragment(n,r));if(r.childNodes&&r.childNodes.length>0){var o=this._store.activeItems,a=this._canAddItem(o,this.input.value);if(a.response)this.choiceList.append(r),this._highlightChoice();else{var c=this._getTemplate("notice",a.notice);this.choiceList.append(c)}}else{var l=void 0;c=void 0,this._isSearching?(c="function"==typeof this.config.noResultsText?this.config.noResultsText():this.config.noResultsText,l=this._getTemplate("notice",c,"no-results")):(c="function"==typeof this.config.noChoicesText?this.config.noChoicesText():this.config.noChoicesText,l=this._getTemplate("notice",c,"no-choices")),this.choiceList.append(l)}},e.prototype._renderItems=function(){var e=this._store.activeItems||[];this.itemList.clear();var t=this._createItemsFragment(e);t.childNodes&&this.itemList.append(t)},e.prototype._createGroupsFragment=function(e,t,i){var n=this;return void 0===i&&(i=document.createDocumentFragment()),this.config.shouldSort&&e.sort(this.config.sorter),e.forEach((function(e){var r=function(e){return t.filter((function(t){return n._isSelectOneElement?t.groupId===e.id:t.groupId===e.id&&("always"===n.config.renderSelectedChoices||!t.selected)}))}(e);if(r.length>=1){var s=n._getTemplate("choiceGroup",e);i.appendChild(s),n._createChoicesFragment(r,i,!0)}})),i},e.prototype._createChoicesFragment=function(e,t,i){var r=this;void 0===t&&(t=document.createDocumentFragment()),void 0===i&&(i=!1);var s=this.config,o=s.renderSelectedChoices,a=s.searchResultLimit,c=s.renderChoiceLimit,l=this._isSearching?f.sortByScore:this.config.sorter,h=function(e){if("auto"!==o||r._isSelectOneElement||!e.selected){var i=r._getTemplate("choice",e,r.config.itemSelectText);t.appendChild(i)}},u=e;"auto"!==o||this._isSelectOneElement||(u=e.filter((function(e){return!e.selected})));var d=u.reduce((function(e,t){return t.placeholder?e.placeholderChoices.push(t):e.normalChoices.push(t),e}),{placeholderChoices:[],normalChoices:[]}),p=d.placeholderChoices,m=d.normalChoices;(this.config.shouldSort||this._isSearching)&&m.sort(l);var v=u.length,g=this._isSelectOneElement?n(n([],p,!0),m,!0):m;this._isSearching?v=a:c&&c>0&&!i&&(v=c);for(var _=0;_<v;_+=1)g[_]&&h(g[_]);return t},e.prototype._createItemsFragment=function(e,t){var i=this;void 0===t&&(t=document.createDocumentFragment());var n=this.config,r=n.shouldSortItems,s=n.sorter,o=n.removeItemButton;return r&&!this._isSelectOneElement&&e.sort(s),this._isTextElement?this.passedElement.value=e.map((function(e){return e.value})).join(this.config.delimiter):this.passedElement.options=e,e.forEach((function(e){var n=i._getTemplate("item",e,o);t.appendChild(n)})),t},e.prototype._triggerChange=function(e){null!=e&&this.passedElement.triggerEvent(d.EVENTS.change,{value:e})},e.prototype._selectPlaceholderChoice=function(e){this._addItem({value:e.value,label:e.label,choiceId:e.id,groupId:e.groupId,placeholder:e.placeholder}),this._triggerChange(e.value)},e.prototype._handleButtonAction=function(e,t){if(e&&t&&this.config.removeItems&&this.config.removeItemButton){var i=t.parentNode&&t.parentNode.dataset.id,n=i&&e.find((function(e){return e.id===parseInt(i,10)}));n&&(this._removeItem(n),this._triggerChange(n.value),this._isSelectOneElement&&this._store.placeholderChoice&&this._selectPlaceholderChoice(this._store.placeholderChoice))}},e.prototype._handleItemAction=function(e,t,i){var n=this;if(void 0===i&&(i=!1),e&&t&&this.config.removeItems&&!this._isSelectOneElement){var r=t.dataset.id;e.forEach((function(e){e.id!==parseInt("".concat(r),10)||e.highlighted?!i&&e.highlighted&&n.unhighlightItem(e):n.highlightItem(e)})),this.input.focus()}},e.prototype._handleChoiceAction=function(e,t){if(e&&t){var i=t.dataset.id,n=i&&this._store.getChoiceById(i);if(n){var r=e[0]&&e[0].keyCode?e[0].keyCode:void 0,s=this.dropdown.isActive;n.keyCode=r,this.passedElement.triggerEvent(d.EVENTS.choice,{choice:n}),n.selected||n.disabled||this._canAddItem(e,n.value).response&&(this._addItem({value:n.value,label:n.label,choiceId:n.id,groupId:n.groupId,customProperties:n.customProperties,placeholder:n.placeholder,keyCode:n.keyCode}),this._triggerChange(n.value)),this.clearInput(),s&&this._isSelectOneElement&&(this.hideDropdown(!0),this.containerOuter.focus())}}},e.prototype._handleBackspace=function(e){if(this.config.removeItems&&e){var t=e[e.length-1],i=e.some((function(e){return e.highlighted}));this.config.editItems&&!i&&t?(this.input.value=t.value,this.input.setWidth(),this._removeItem(t),this._triggerChange(t.value)):(i||this.highlightItem(t,!1),this.removeHighlightedItems(!0))}},e.prototype._startLoading=function(){this._store.dispatch((0,h.setIsLoading)(!0))},e.prototype._stopLoading=function(){this._store.dispatch((0,h.setIsLoading)(!1))},e.prototype._handleLoadingState=function(e){void 0===e&&(e=!0);var t=this.itemList.getChild(".".concat(this.config.classNames.placeholder));e?(this.disable(),this.containerOuter.addLoadingState(),this._isSelectOneElement?t?t.innerHTML=this.config.loadingText:(t=this._getTemplate("placeholder",this.config.loadingText))&&this.itemList.append(t):this.input.placeholder=this.config.loadingText):(this.enable(),this.containerOuter.removeLoadingState(),this._isSelectOneElement?t&&(t.innerHTML=this._placeholderValue||""):this.input.placeholder=this._placeholderValue||"")},e.prototype._handleSearch=function(e){if(this.input.isFocussed){var t=this._store.choices,i=this.config,n=i.searchFloor,r=i.searchChoices,s=t.some((function(e){return!e.active}));if(null!=e&&e.length>=n){var o=r?this._searchChoices(e):0;this.passedElement.triggerEvent(d.EVENTS.search,{value:e,resultCount:o})}else s&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0)))}},e.prototype._canAddItem=function(e,t){var i=!0,n="function"==typeof this.config.addItemText?this.config.addItemText(t):this.config.addItemText;if(!this._isSelectOneElement){var r=(0,f.existsInArray)(e,t);this.config.maxItemCount>0&&this.config.maxItemCount<=e.length&&(i=!1,n="function"==typeof this.config.maxItemText?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),!this.config.duplicateItemsAllowed&&r&&i&&(i=!1,n="function"==typeof this.config.uniqueItemText?this.config.uniqueItemText(t):this.config.uniqueItemText),this._isTextElement&&this.config.addItems&&i&&"function"==typeof this.config.addItemFilter&&!this.config.addItemFilter(t)&&(i=!1,n="function"==typeof this.config.customAddItemText?this.config.customAddItemText(t):this.config.customAddItemText)}return{response:i,notice:n}},e.prototype._searchChoices=function(e){var t="string"==typeof e?e.trim():e,i="string"==typeof this._currentValue?this._currentValue.trim():this._currentValue;if(t.length<1&&t==="".concat(i," "))return 0;var r=this._store.searchableChoices,s=t,c=Object.assign(this.config.fuseOptions,{keys:n([],this.config.searchFields,!0),includeMatches:!0}),l=new o.default(r,c).search(s);return this._currentValue=t,this._highlightPosition=0,this._isSearching=!0,this._store.dispatch((0,a.filterChoices)(l)),l.length},e.prototype._addEventListeners=function(){var e=document.documentElement;e.addEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.addEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.addEventListener("mousedown",this._onMouseDown,!0),e.addEventListener("click",this._onClick,{passive:!0}),e.addEventListener("touchmove",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener("mouseover",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(this.containerOuter.element.addEventListener("focus",this._onFocus,{passive:!0}),this.containerOuter.element.addEventListener("blur",this._onBlur,{passive:!0})),this.input.element.addEventListener("keyup",this._onKeyUp,{passive:!0}),this.input.element.addEventListener("focus",this._onFocus,{passive:!0}),this.input.element.addEventListener("blur",this._onBlur,{passive:!0}),this.input.element.form&&this.input.element.form.addEventListener("reset",this._onFormReset,{passive:!0}),this.input.addEventListeners()},e.prototype._removeEventListeners=function(){var e=document.documentElement;e.removeEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.removeEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.removeEventListener("mousedown",this._onMouseDown,!0),e.removeEventListener("click",this._onClick),e.removeEventListener("touchmove",this._onTouchMove),this.dropdown.element.removeEventListener("mouseover",this._onMouseOver),this._isSelectOneElement&&(this.containerOuter.element.removeEventListener("focus",this._onFocus),this.containerOuter.element.removeEventListener("blur",this._onBlur)),this.input.element.removeEventListener("keyup",this._onKeyUp),this.input.element.removeEventListener("focus",this._onFocus),this.input.element.removeEventListener("blur",this._onBlur),this.input.element.form&&this.input.element.form.removeEventListener("reset",this._onFormReset),this.input.removeEventListeners()},e.prototype._onKeyDown=function(e){var t=e.keyCode,i=this._store.activeItems,n=this.input.isFocussed,r=this.dropdown.isActive,s=this.itemList.hasChildren(),o=String.fromCharCode(t),a=/[^\x00-\x1F]/.test(o),c=d.KEY_CODES.BACK_KEY,l=d.KEY_CODES.DELETE_KEY,h=d.KEY_CODES.ENTER_KEY,u=d.KEY_CODES.A_KEY,p=d.KEY_CODES.ESC_KEY,f=d.KEY_CODES.UP_KEY,m=d.KEY_CODES.DOWN_KEY,v=d.KEY_CODES.PAGE_UP_KEY,g=d.KEY_CODES.PAGE_DOWN_KEY;switch(this._isTextElement||r||!a||(this.showDropdown(),this.input.isFocussed||(this.input.value+=e.key.toLowerCase())),t){case u:return this._onSelectKey(e,s);case h:return this._onEnterKey(e,i,r);case p:return this._onEscapeKey(r);case f:case v:case m:case g:return this._onDirectionKey(e,r);case l:case c:return this._onDeleteKey(e,i,n)}},e.prototype._onKeyUp=function(e){var t=e.target,i=e.keyCode,n=this.input.value,r=this._store.activeItems,s=this._canAddItem(r,n),o=d.KEY_CODES.BACK_KEY,c=d.KEY_CODES.DELETE_KEY;if(this._isTextElement)if(s.notice&&n){var l=this._getTemplate("notice",s.notice);this.dropdown.element.innerHTML=l.outerHTML,this.showDropdown(!0)}else this.hideDropdown(!0);else{var h=(i===o||i===c)&&t&&!t.value,u=!this._isTextElement&&this._isSearching,p=this._canSearch&&s.response;h&&u?(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))):p&&this._handleSearch(this.input.rawValue)}this._canSearch=this.config.searchEnabled},e.prototype._onSelectKey=function(e,t){var i=e.ctrlKey,n=e.metaKey;(i||n)&&t&&(this._canSearch=!1,this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement&&this.highlightAll())},e.prototype._onEnterKey=function(e,t,i){var n=e.target,r=d.KEY_CODES.ENTER_KEY,s=n&&n.hasAttribute("data-button");if(this._isTextElement&&n&&n.value){var o=this.input.value;this._canAddItem(t,o).response&&(this.hideDropdown(!0),this._addItem({value:o}),this._triggerChange(o),this.clearInput())}if(s&&(this._handleButtonAction(t,n),e.preventDefault()),i){var a=this.dropdown.getChild(".".concat(this.config.classNames.highlightedState));a&&(t[0]&&(t[0].keyCode=r),this._handleChoiceAction(t,a)),e.preventDefault()}else this._isSelectOneElement&&(this.showDropdown(),e.preventDefault())},e.prototype._onEscapeKey=function(e){e&&(this.hideDropdown(!0),this.containerOuter.focus())},e.prototype._onDirectionKey=function(e,t){var i=e.keyCode,n=e.metaKey,r=d.KEY_CODES.DOWN_KEY,s=d.KEY_CODES.PAGE_UP_KEY,o=d.KEY_CODES.PAGE_DOWN_KEY;if(t||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var a=i===r||i===o?1:-1,c="[data-choice-selectable]",l=void 0;if(n||i===o||i===s)l=a>0?this.dropdown.element.querySelector("".concat(c,":last-of-type")):this.dropdown.element.querySelector(c);else{var h=this.dropdown.element.querySelector(".".concat(this.config.classNames.highlightedState));l=h?(0,f.getAdjacentEl)(h,c,a):this.dropdown.element.querySelector(c)}l&&((0,f.isScrolledIntoView)(l,this.choiceList.element,a)||this.choiceList.scrollToChildElement(l,a),this._highlightChoice(l)),e.preventDefault()}},e.prototype._onDeleteKey=function(e,t,i){var n=e.target;this._isSelectOneElement||n.value||!i||(this._handleBackspace(t),e.preventDefault())},e.prototype._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},e.prototype._onTouchEnd=function(e){var t=(e||e.touches[0]).target;this._wasTap&&this.containerOuter.element.contains(t)&&((t===this.containerOuter.element||t===this.containerInner.element)&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),e.stopPropagation()),this._wasTap=!0},e.prototype._onMouseDown=function(e){var t=e.target;if(t instanceof HTMLElement){if(_&&this.choiceList.element.contains(t)){var i=this.choiceList.element.firstElementChild,n="ltr"===this._direction?e.offsetX>=i.offsetWidth:e.offsetX<i.offsetLeft;this._isScrollingOnIe=n}if(t!==this.input.element){var r=t.closest("[data-button],[data-item],[data-choice]");if(r instanceof HTMLElement){var s=e.shiftKey,o=this._store.activeItems,a=r.dataset;"button"in a?this._handleButtonAction(o,r):"item"in a?this._handleItemAction(o,r,s):"choice"in a&&this._handleChoiceAction(o,r)}e.preventDefault()}}},e.prototype._onMouseOver=function(e){var t=e.target;t instanceof HTMLElement&&"choice"in t.dataset&&this._highlightChoice(t)},e.prototype._onClick=function(e){var t=e.target;this.containerOuter.element.contains(t)?this.dropdown.isActive||this.containerOuter.isDisabled?this._isSelectOneElement&&t!==this.input.element&&!this.dropdown.element.contains(t)&&this.hideDropdown():this._isTextElement?document.activeElement!==this.input.element&&this.input.focus():(this.showDropdown(),this.containerOuter.focus()):(this._store.highlightedActiveItems.length>0&&this.unhighlightAll(),this.containerOuter.removeFocusState(),this.hideDropdown(!0))},e.prototype._onFocus=function(e){var t,i=this,n=e.target;n&&this.containerOuter.element.contains(n)&&((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&i.containerOuter.addFocusState()},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.addFocusState(),n===i.input.element&&i.showDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.showDropdown(!0),i.containerOuter.addFocusState())},t)[this.passedElement.element.type]()},e.prototype._onBlur=function(e){var t,i=this,n=e.target;if(n&&this.containerOuter.element.contains(n)&&!this._isScrollingOnIe){var r=this._store.activeItems.some((function(e){return e.highlighted}));((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),r&&i.unhighlightAll(),i.hideDropdown(!0))},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.removeFocusState(),(n===i.input.element||n===i.containerOuter.element&&!i._canSearch)&&i.hideDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),i.hideDropdown(!0),r&&i.unhighlightAll())},t)[this.passedElement.element.type]()}else this._isScrollingOnIe=!1,this.input.element.focus()},e.prototype._onFormReset=function(){this._store.dispatch((0,h.resetTo)(this._initialState))},e.prototype._highlightChoice=function(e){var t=this;void 0===e&&(e=null);var i=Array.from(this.dropdown.element.querySelectorAll("[data-choice-selectable]"));if(i.length){var n=e;Array.from(this.dropdown.element.querySelectorAll(".".concat(this.config.classNames.highlightedState))).forEach((function(e){e.classList.remove(t.config.classNames.highlightedState),e.setAttribute("aria-selected","false")})),n?this._highlightPosition=i.indexOf(n):(n=i.length>this._highlightPosition?i[this._highlightPosition]:i[i.length-1])||(n=i[0]),n.classList.add(this.config.classNames.highlightedState),n.setAttribute("aria-selected","true"),this.passedElement.triggerEvent(d.EVENTS.highlightChoice,{el:n}),this.dropdown.isActive&&(this.input.setActiveDescendant(n.id),this.containerOuter.setActiveDescendant(n.id))}},e.prototype._addItem=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.choiceId,s=void 0===r?-1:r,o=e.groupId,a=void 0===o?-1:o,c=e.customProperties,h=void 0===c?{}:c,u=e.placeholder,p=void 0!==u&&u,f=e.keyCode,m=void 0===f?-1:f,v="string"==typeof t?t.trim():t,g=this._store.items,_=n||v,y=s||-1,E=a>=0?this._store.getGroupById(a):null,b=g?g.length+1:1;this.config.prependValue&&(v=this.config.prependValue+v.toString()),this.config.appendValue&&(v+=this.config.appendValue.toString()),this._store.dispatch((0,l.addItem)({value:v,label:_,id:b,choiceId:y,groupId:a,customProperties:h,placeholder:p,keyCode:m})),this._isSelectOneElement&&this.removeActiveItems(b),this.passedElement.triggerEvent(d.EVENTS.addItem,{id:b,value:v,label:_,customProperties:h,groupValue:E&&E.value?E.value:null,keyCode:m})},e.prototype._removeItem=function(e){var t=e.id,i=e.value,n=e.label,r=e.customProperties,s=e.choiceId,o=e.groupId,a=o&&o>=0?this._store.getGroupById(o):null;t&&s&&(this._store.dispatch((0,l.removeItem)(t,s)),this.passedElement.triggerEvent(d.EVENTS.removeItem,{id:t,value:i,label:n,customProperties:r,groupValue:a&&a.value?a.value:null}))},e.prototype._addChoice=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.isSelected,s=void 0!==r&&r,o=e.isDisabled,c=void 0!==o&&o,l=e.groupId,h=void 0===l?-1:l,u=e.customProperties,d=void 0===u?{}:u,p=e.placeholder,f=void 0!==p&&p,m=e.keyCode,v=void 0===m?-1:m;if(null!=t){var g=this._store.choices,_=n||t,y=g?g.length+1:1,E="".concat(this._baseId,"-").concat(this._idNames.itemChoice,"-").concat(y);this._store.dispatch((0,a.addChoice)({id:y,groupId:h,elementId:E,value:t,label:_,disabled:c,customProperties:d,placeholder:f,keyCode:v})),s&&this._addItem({value:t,label:_,choiceId:y,customProperties:d,placeholder:f,keyCode:v})}},e.prototype._addGroup=function(e){var t=this,i=e.group,n=e.id,r=e.valueKey,s=void 0===r?"value":r,o=e.labelKey,a=void 0===o?"label":o,l=(0,f.isType)("Object",i)?i.choices:Array.from(i.getElementsByTagName("OPTION")),h=n||Math.floor((new Date).valueOf()*Math.random()),u=!!i.disabled&&i.disabled;l?(this._store.dispatch((0,c.addGroup)({value:i.label,id:h,active:!0,disabled:u})),l.forEach((function(e){var i=e.disabled||e.parentNode&&e.parentNode.disabled;t._addChoice({value:e[s],label:(0,f.isType)("Object",e)?e[a]:e.innerHTML,isSelected:e.selected,isDisabled:i,groupId:h,customProperties:e.customProperties,placeholder:e.placeholder})}))):this._store.dispatch((0,c.addGroup)({value:i.label,id:i.id,active:!1,disabled:i.disabled}))},e.prototype._getTemplate=function(e){for(var t,i=[],r=1;r<arguments.length;r++)i[r-1]=arguments[r];return(t=this._templates[e]).call.apply(t,n([this,this.config],i,!1))},e.prototype._createTemplates=function(){var e=this.config.callbackOnCreateTemplates,t={};e&&"function"==typeof e&&(t=e.call(this,f.strToEl)),this._templates=(0,s.default)(g.default,t)},e.prototype._createElements=function(){this.containerOuter=new u.Container({element:this._getTemplate("containerOuter",this._direction,this._isSelectElement,this._isSelectOneElement,this.config.searchEnabled,this.passedElement.element.type,this.config.labelId),classNames:this.config.classNames,type:this.passedElement.element.type,position:this.config.position}),this.containerInner=new u.Container({element:this._getTemplate("containerInner"),classNames:this.config.classNames,type:this.passedElement.element.type,position:this.config.position}),this.input=new u.Input({element:this._getTemplate("input",this._placeholderValue),classNames:this.config.classNames,type:this.passedElement.element.type,preventPaste:!this.config.paste}),this.choiceList=new u.List({element:this._getTemplate("choiceList",this._isSelectOneElement)}),this.itemList=new u.List({element:this._getTemplate("itemList",this._isSelectOneElement)}),this.dropdown=new u.Dropdown({element:this._getTemplate("dropdown"),classNames:this.config.classNames,type:this.passedElement.element.type})},e.prototype._createStructure=function(){this.passedElement.conceal(),this.containerInner.wrap(this.passedElement.element),this.containerOuter.wrap(this.containerInner.element),this._isSelectOneElement?this.input.placeholder=this.config.searchPlaceholderValue||"":this._placeholderValue&&(this.input.placeholder=this._placeholderValue,this.input.setWidth()),this.containerOuter.element.appendChild(this.containerInner.element),this.containerOuter.element.appendChild(this.dropdown.element),this.containerInner.element.appendChild(this.itemList.element),this._isTextElement||this.dropdown.element.appendChild(this.choiceList.element),this._isSelectOneElement?this.config.searchEnabled&&this.dropdown.element.insertBefore(this.input.element,this.dropdown.element.firstChild):this.containerInner.element.appendChild(this.input.element),this._isSelectElement&&(this._highlightPosition=0,this._isSearching=!1,this._startLoading(),this._presetGroups.length?this._addPredefinedGroups(this._presetGroups):this._addPredefinedChoices(this._presetChoices),this._stopLoading()),this._isTextElement&&this._addPredefinedItems(this._presetItems)},e.prototype._addPredefinedGroups=function(e){var t=this,i=this.passedElement.placeholderOption;i&&i.parentNode&&"SELECT"===i.parentNode.tagName&&this._addChoice({value:i.value,label:i.innerHTML,isSelected:i.selected,isDisabled:i.disabled,placeholder:!0}),e.forEach((function(e){return t._addGroup({group:e,id:e.id||null})}))},e.prototype._addPredefinedChoices=function(e){var t=this;this.config.shouldSort&&e.sort(this.config.sorter);var i=e.some((function(e){return e.selected})),n=e.findIndex((function(e){return void 0===e.disabled||!e.disabled}));e.forEach((function(e,r){var s=e.value,o=void 0===s?"":s,a=e.label,c=e.customProperties,l=e.placeholder;if(t._isSelectElement)if(e.choices)t._addGroup({group:e,id:e.id||null});else{var h=!(!t._isSelectOneElement||i||r!==n)||e.selected,u=e.disabled;t._addChoice({value:o,label:a,isSelected:!!h,isDisabled:!!u,placeholder:!!l,customProperties:c})}else t._addChoice({value:o,label:a,isSelected:!!e.selected,isDisabled:!!e.disabled,placeholder:!!e.placeholder,customProperties:c})}))},e.prototype._addPredefinedItems=function(e){var t=this;e.forEach((function(e){"object"==typeof e&&e.value&&t._addItem({value:e.value,label:e.label,choiceId:e.id,customProperties:e.customProperties,placeholder:e.placeholder}),"string"==typeof e&&t._addItem({value:e})}))},e.prototype._setChoiceOrItem=function(e){var t=this;({object:function(){e.value&&(t._isTextElement?t._addItem({value:e.value,label:e.label,choiceId:e.id,customProperties:e.customProperties,placeholder:e.placeholder}):t._addChoice({value:e.value,label:e.label,isSelected:!0,isDisabled:!1,customProperties:e.customProperties,placeholder:e.placeholder}))},string:function(){t._isTextElement?t._addItem({value:e}):t._addChoice({value:e,label:e,isSelected:!0,isDisabled:!1})}})[(0,f.getType)(e).toLowerCase()]()},e.prototype._findAndSelectChoiceByValue=function(e){var t=this,i=this._store.choices.find((function(i){return t.config.valueComparer(i.value,e)}));i&&!i.selected&&this._addItem({value:i.value,label:i.label,choiceId:i.id,groupId:i.groupId,customProperties:i.customProperties,placeholder:i.placeholder,keyCode:i.keyCode})},e.prototype._generatePlaceholderValue=function(){if(this._isSelectElement&&this.passedElement.placeholderOption){var e=this.passedElement.placeholderOption;return e?e.text:null}var t=this.config,i=t.placeholder,n=t.placeholderValue,r=this.passedElement.element.dataset;if(i){if(n)return n;if(r.placeholder)return r.placeholder}return null},e}();t.default=E},613:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0});var n=i(799),r=i(883),s=function(){function e(e){var t=e.element,i=e.type,n=e.classNames,r=e.position;this.element=t,this.classNames=n,this.type=i,this.position=r,this.isOpen=!1,this.isFlipped=!1,this.isFocussed=!1,this.isDisabled=!1,this.isLoading=!1,this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this)}return e.prototype.addEventListeners=function(){this.element.addEventListener("focus",this._onFocus),this.element.addEventListener("blur",this._onBlur)},e.prototype.removeEventListeners=function(){this.element.removeEventListener("focus",this._onFocus),this.element.removeEventListener("blur",this._onBlur)},e.prototype.shouldFlip=function(e){if("number"!=typeof e)return!1;var t=!1;return"auto"===this.position?t=!window.matchMedia("(min-height: ".concat(e+1,"px)")).matches:"top"===this.position&&(t=!0),t},e.prototype.setActiveDescendant=function(e){this.element.setAttribute("aria-activedescendant",e)},e.prototype.removeActiveDescendant=function(){this.element.removeAttribute("aria-activedescendant")},e.prototype.open=function(e){this.element.classList.add(this.classNames.openState),this.element.setAttribute("aria-expanded","true"),this.isOpen=!0,this.shouldFlip(e)&&(this.element.classList.add(this.classNames.flippedState),this.isFlipped=!0)},e.prototype.close=function(){this.element.classList.remove(this.classNames.openState),this.element.setAttribute("aria-expanded","false"),this.removeActiveDescendant(),this.isOpen=!1,this.isFlipped&&(this.element.classList.remove(this.classNames.flippedState),this.isFlipped=!1)},e.prototype.focus=function(){this.isFocussed||this.element.focus()},e.prototype.addFocusState=function(){this.element.classList.add(this.classNames.focusState)},e.prototype.removeFocusState=function(){this.element.classList.remove(this.classNames.focusState)},e.prototype.enable=function(){this.element.classList.remove(this.classNames.disabledState),this.element.removeAttribute("aria-disabled"),this.type===r.SELECT_ONE_TYPE&&this.element.setAttribute("tabindex","0"),this.isDisabled=!1},e.prototype.disable=function(){this.element.classList.add(this.classNames.disabledState),this.element.setAttribute("aria-disabled","true"),this.type===r.SELECT_ONE_TYPE&&this.element.setAttribute("tabindex","-1"),this.isDisabled=!0},e.prototype.wrap=function(e){(0,n.wrap)(e,this.element)},e.prototype.unwrap=function(e){this.element.parentNode&&(this.element.parentNode.insertBefore(e,this.element),this.element.parentNode.removeChild(this.element))},e.prototype.addLoadingState=function(){this.element.classList.add(this.classNames.loadingState),this.element.setAttribute("aria-busy","true"),this.isLoading=!0},e.prototype.removeLoadingState=function(){this.element.classList.remove(this.classNames.loadingState),this.element.removeAttribute("aria-busy"),this.isLoading=!1},e.prototype._onFocus=function(){this.isFocussed=!0},e.prototype._onBlur=function(){this.isFocussed=!1},e}();t.default=s},217:function(e,t){Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e){var t=e.element,i=e.type,n=e.classNames;this.element=t,this.classNames=n,this.type=i,this.isActive=!1}return Object.defineProperty(e.prototype,"distanceFromTopWindow",{get:function(){return this.element.getBoundingClientRect().bottom},enumerable:!1,configurable:!0}),e.prototype.getChild=function(e){return this.element.querySelector(e)},e.prototype.show=function(){return this.element.classList.add(this.classNames.activeState),this.element.setAttribute("aria-expanded","true"),this.isActive=!0,this},e.prototype.hide=function(){return this.element.classList.remove(this.classNames.activeState),this.element.setAttribute("aria-expanded","false"),this.isActive=!1,this},e}();t.default=i},520:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.WrappedSelect=t.WrappedInput=t.List=t.Input=t.Container=t.Dropdown=void 0;var r=n(i(217));t.Dropdown=r.default;var s=n(i(613));t.Container=s.default;var o=n(i(11));t.Input=o.default;var a=n(i(624));t.List=a.default;var c=n(i(541));t.WrappedInput=c.default;var l=n(i(982));t.WrappedSelect=l.default},11:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0});var n=i(799),r=i(883),s=function(){function e(e){var t=e.element,i=e.type,n=e.classNames,r=e.preventPaste;this.element=t,this.type=i,this.classNames=n,this.preventPaste=r,this.isFocussed=this.element.isEqualNode(document.activeElement),this.isDisabled=t.disabled,this._onPaste=this._onPaste.bind(this),this._onInput=this._onInput.bind(this),this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this)}return Object.defineProperty(e.prototype,"placeholder",{set:function(e){this.element.placeholder=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"value",{get:function(){return(0,n.sanitise)(this.element.value)},set:function(e){this.element.value=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"rawValue",{get:function(){return this.element.value},enumerable:!1,configurable:!0}),e.prototype.addEventListeners=function(){this.element.addEventListener("paste",this._onPaste),this.element.addEventListener("input",this._onInput,{passive:!0}),this.element.addEventListener("focus",this._onFocus,{passive:!0}),this.element.addEventListener("blur",this._onBlur,{passive:!0})},e.prototype.removeEventListeners=function(){this.element.removeEventListener("input",this._onInput),this.element.removeEventListener("paste",this._onPaste),this.element.removeEventListener("focus",this._onFocus),this.element.removeEventListener("blur",this._onBlur)},e.prototype.enable=function(){this.element.removeAttribute("disabled"),this.isDisabled=!1},e.prototype.disable=function(){this.element.setAttribute("disabled",""),this.isDisabled=!0},e.prototype.focus=function(){this.isFocussed||this.element.focus()},e.prototype.blur=function(){this.isFocussed&&this.element.blur()},e.prototype.clear=function(e){return void 0===e&&(e=!0),this.element.value&&(this.element.value=""),e&&this.setWidth(),this},e.prototype.setWidth=function(){var e=this.element,t=e.style,i=e.value,n=e.placeholder;t.minWidth="".concat(n.length+1,"ch"),t.width="".concat(i.length+1,"ch")},e.prototype.setActiveDescendant=function(e){this.element.setAttribute("aria-activedescendant",e)},e.prototype.removeActiveDescendant=function(){this.element.removeAttribute("aria-activedescendant")},e.prototype._onInput=function(){this.type!==r.SELECT_ONE_TYPE&&this.setWidth()},e.prototype._onPaste=function(e){this.preventPaste&&e.preventDefault()},e.prototype._onFocus=function(){this.isFocussed=!0},e.prototype._onBlur=function(){this.isFocussed=!1},e}();t.default=s},624:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0});var n=i(883),r=function(){function e(e){var t=e.element;this.element=t,this.scrollPos=this.element.scrollTop,this.height=this.element.offsetHeight}return e.prototype.clear=function(){this.element.innerHTML=""},e.prototype.append=function(e){this.element.appendChild(e)},e.prototype.getChild=function(e){return this.element.querySelector(e)},e.prototype.hasChildren=function(){return this.element.hasChildNodes()},e.prototype.scrollToTop=function(){this.element.scrollTop=0},e.prototype.scrollToChildElement=function(e,t){var i=this;if(e){var n=this.element.offsetHeight,r=this.element.scrollTop+n,s=e.offsetHeight,o=e.offsetTop+s,a=t>0?this.element.scrollTop+o-r:e.offsetTop;requestAnimationFrame((function(){i._animateScroll(a,t)}))}},e.prototype._scrollDown=function(e,t,i){var n=(i-e)/t,r=n>1?n:1;this.element.scrollTop=e+r},e.prototype._scrollUp=function(e,t,i){var n=(e-i)/t,r=n>1?n:1;this.element.scrollTop=e-r},e.prototype._animateScroll=function(e,t){var i=this,r=n.SCROLLING_SPEED,s=this.element.scrollTop,o=!1;t>0?(this._scrollDown(s,r,e),s<e&&(o=!0)):(this._scrollUp(s,r,e),s>e&&(o=!0)),o&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}();t.default=r},730:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0});var n=i(799),r=function(){function e(e){var t=e.element,i=e.classNames;if(this.element=t,this.classNames=i,!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement))throw new TypeError("Invalid element passed");this.isDisabled=!1}return Object.defineProperty(e.prototype,"isActive",{get:function(){return"active"===this.element.dataset.choice},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"dir",{get:function(){return this.element.dir},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.value=e},enumerable:!1,configurable:!0}),e.prototype.conceal=function(){this.element.classList.add(this.classNames.input),this.element.hidden=!0,this.element.tabIndex=-1;var e=this.element.getAttribute("style");e&&this.element.setAttribute("data-choice-orig-style",e),this.element.setAttribute("data-choice","active")},e.prototype.reveal=function(){this.element.classList.remove(this.classNames.input),this.element.hidden=!1,this.element.removeAttribute("tabindex");var e=this.element.getAttribute("data-choice-orig-style");e?(this.element.removeAttribute("data-choice-orig-style"),this.element.setAttribute("style",e)):this.element.removeAttribute("style"),this.element.removeAttribute("data-choice"),this.element.value=this.element.value},e.prototype.enable=function(){this.element.removeAttribute("disabled"),this.element.disabled=!1,this.isDisabled=!1},e.prototype.disable=function(){this.element.setAttribute("disabled",""),this.element.disabled=!0,this.isDisabled=!0},e.prototype.triggerEvent=function(e,t){(0,n.dispatchEvent)(this.element,e,t)},e}();t.default=r},541:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.delimiter,s=e.call(this,{element:i,classNames:n})||this;return s.delimiter=r,s}return r(t,e),Object.defineProperty(t.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.setAttribute("value",e),this.element.value=e},enumerable:!1,configurable:!0}),t}(s(i(730)).default);t.default=o},982:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.template,s=e.call(this,{element:i,classNames:n})||this;return s.template=r,s}return r(t,e),Object.defineProperty(t.prototype,"placeholderOption",{get:function(){return this.element.querySelector('option[value=""]')||this.element.querySelector("option[placeholder]")},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"optionGroups",{get:function(){return Array.from(this.element.getElementsByTagName("OPTGROUP"))},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"options",{get:function(){return Array.from(this.element.options)},set:function(e){var t=this,i=document.createDocumentFragment();e.forEach((function(e){return n=e,r=t.template(n),void i.appendChild(r);var n,r})),this.appendDocFragment(i)},enumerable:!1,configurable:!0}),t.prototype.appendDocFragment=function(e){this.element.innerHTML="",this.element.appendChild(e)},t}(s(i(730)).default);t.default=o},883:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.SCROLLING_SPEED=t.SELECT_MULTIPLE_TYPE=t.SELECT_ONE_TYPE=t.TEXT_TYPE=t.KEY_CODES=t.ACTION_TYPES=t.EVENTS=void 0,t.EVENTS={showDropdown:"showDropdown",hideDropdown:"hideDropdown",change:"change",choice:"choice",search:"search",addItem:"addItem",removeItem:"removeItem",highlightItem:"highlightItem",highlightChoice:"highlightChoice",unhighlightItem:"unhighlightItem"},t.ACTION_TYPES={ADD_CHOICE:"ADD_CHOICE",FILTER_CHOICES:"FILTER_CHOICES",ACTIVATE_CHOICES:"ACTIVATE_CHOICES",CLEAR_CHOICES:"CLEAR_CHOICES",ADD_GROUP:"ADD_GROUP",ADD_ITEM:"ADD_ITEM",REMOVE_ITEM:"REMOVE_ITEM",HIGHLIGHT_ITEM:"HIGHLIGHT_ITEM",CLEAR_ALL:"CLEAR_ALL",RESET_TO:"RESET_TO",SET_IS_LOADING:"SET_IS_LOADING"},t.KEY_CODES={BACK_KEY:46,DELETE_KEY:8,ENTER_KEY:13,A_KEY:65,ESC_KEY:27,UP_KEY:38,DOWN_KEY:40,PAGE_UP_KEY:33,PAGE_DOWN_KEY:34},t.TEXT_TYPE="text",t.SELECT_ONE_TYPE="select-one",t.SELECT_MULTIPLE_TYPE="select-multiple",t.SCROLLING_SPEED=4},789:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CONFIG=t.DEFAULT_CLASSNAMES=void 0;var n=i(799);t.DEFAULT_CLASSNAMES={containerOuter:"choices",containerInner:"choices__inner",input:"choices__input",inputCloned:"choices__input--cloned",list:"choices__list",listItems:"choices__list--multiple",listSingle:"choices__list--single",listDropdown:"choices__list--dropdown",item:"choices__item",itemSelectable:"choices__item--selectable",itemDisabled:"choices__item--disabled",itemChoice:"choices__item--choice",placeholder:"choices__placeholder",group:"choices__group",groupHeading:"choices__heading",button:"choices__button",activeState:"is-active",focusState:"is-focused",openState:"is-open",disabledState:"is-disabled",highlightedState:"is-highlighted",selectedState:"is-selected",flippedState:"is-flipped",loadingState:"is-loading",noResults:"has-no-results",noChoices:"has-no-choices"},t.DEFAULT_CONFIG={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,addItems:!0,addItemFilter:null,removeItems:!0,removeItemButton:!1,editItems:!1,allowHTML:!0,duplicateItemsAllowed:!0,delimiter:",",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:["label","value"],position:"auto",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sorter:n.sortByAlpha,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:"auto",loadingText:"Loading...",noResultsText:"No results found",noChoicesText:"No choices to choose from",itemSelectText:"Press to select",uniqueItemText:"Only unique values can be added",customAddItemText:"Only values matching specific conditions can be added",addItemText:function(e){return'Press Enter to add <b>"'.concat((0,n.sanitise)(e),'"</b>')},maxItemText:function(e){return"Only ".concat(e," values can be added")},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},labelId:"",callbackOnInit:null,callbackOnCreateTemplates:null,classNames:t.DEFAULT_CLASSNAMES}},18:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},978:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},948:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},359:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},285:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},533:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},187:function(e,t,i){var n=this&&this.__createBinding||(Object.create?function(e,t,i,n){void 0===n&&(n=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,n,r)}:function(e,t,i,n){void 0===n&&(n=i),e[n]=t[i]}),r=this&&this.__exportStar||function(e,t){for(var i in e)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||n(t,e,i)};Object.defineProperty(t,"__esModule",{value:!0}),r(i(18),t),r(i(978),t),r(i(948),t),r(i(359),t),r(i(285),t),r(i(533),t),r(i(287),t),r(i(132),t),r(i(837),t),r(i(598),t),r(i(369),t),r(i(37),t),r(i(47),t),r(i(923),t),r(i(876),t)},287:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},132:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},837:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},598:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},37:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},369:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},47:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},923:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},876:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},799:function(e,t){var i;Object.defineProperty(t,"__esModule",{value:!0}),t.parseCustomProperties=t.diff=t.cloneObject=t.existsInArray=t.dispatchEvent=t.sortByScore=t.sortByAlpha=t.strToEl=t.sanitise=t.isScrolledIntoView=t.getAdjacentEl=t.wrap=t.isType=t.getType=t.generateId=t.generateChars=t.getRandomNumber=void 0,t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)},t.generateChars=function(e){return Array.from({length:e},(function(){return(0,t.getRandomNumber)(0,36).toString(36)})).join("")},t.generateId=function(e,i){var n=e.id||e.name&&"".concat(e.name,"-").concat((0,t.generateChars)(2))||(0,t.generateChars)(4);return n=n.replace(/(:|\.|\[|\]|,)/g,""),"".concat(i,"-").concat(n)},t.getType=function(e){return Object.prototype.toString.call(e).slice(8,-1)},t.isType=function(e,i){return null!=i&&(0,t.getType)(i)===e},t.wrap=function(e,t){return void 0===t&&(t=document.createElement("div")),e.parentNode&&(e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t)),t.appendChild(e)},t.getAdjacentEl=function(e,t,i){void 0===i&&(i=1);for(var n="".concat(i>0?"next":"previous","ElementSibling"),r=e[n];r;){if(r.matches(t))return r;r=r[n]}return r},t.isScrolledIntoView=function(e,t,i){return void 0===i&&(i=1),!!e&&(i>0?t.scrollTop+t.offsetHeight>=e.offsetTop+e.offsetHeight:e.offsetTop>=t.scrollTop)},t.sanitise=function(e){return"string"!=typeof e?e:e.replace(/&/g,"&amp;").replace(/>/g,"&gt;").replace(/</g,"&lt;").replace(/"/g,"&quot;")},t.strToEl=(i=document.createElement("div"),function(e){var t=e.trim();i.innerHTML=t;for(var n=i.children[0];i.firstChild;)i.removeChild(i.firstChild);return n}),t.sortByAlpha=function(e,t){var i=e.value,n=e.label,r=void 0===n?i:n,s=t.value,o=t.label,a=void 0===o?s:o;return r.localeCompare(a,[],{sensitivity:"base",ignorePunctuation:!0,numeric:!0})},t.sortByScore=function(e,t){var i=e.score,n=void 0===i?0:i,r=t.score;return n-(void 0===r?0:r)},t.dispatchEvent=function(e,t,i){void 0===i&&(i=null);var n=new CustomEvent(t,{detail:i,bubbles:!0,cancelable:!0});return e.dispatchEvent(n)},t.existsInArray=function(e,t,i){return void 0===i&&(i="value"),e.some((function(e){return"string"==typeof t?e[i]===t.trim():e[i]===t}))},t.cloneObject=function(e){return JSON.parse(JSON.stringify(e))},t.diff=function(e,t){var i=Object.keys(e).sort(),n=Object.keys(t).sort();return i.filter((function(e){return n.indexOf(e)<0}))},t.parseCustomProperties=function(e){if(void 0!==e)try{return JSON.parse(e)}catch(t){return e}return{}}},273:function(e,t){var i=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r<s;r++)!n&&r in t||(n||(n=Array.prototype.slice.call(t,0,r)),n[r]=t[r]);return e.concat(n||Array.prototype.slice.call(t))};Object.defineProperty(t,"__esModule",{value:!0}),t.defaultState=void 0,t.defaultState=[],t.default=function(e,n){switch(void 0===e&&(e=t.defaultState),void 0===n&&(n={}),n.type){case"ADD_CHOICE":var r=n,s={id:r.id,elementId:r.elementId,groupId:r.groupId,value:r.value,label:r.label||r.value,disabled:r.disabled||!1,selected:!1,active:!0,score:9999,customProperties:r.customProperties,placeholder:r.placeholder||!1};return i(i([],e,!0),[s],!1);case"ADD_ITEM":var o=n;return o.choiceId>-1?e.map((function(e){var t=e;return t.id===parseInt("".concat(o.choiceId),10)&&(t.selected=!0),t})):e;case"REMOVE_ITEM":var a=n;return a.choiceId&&a.choiceId>-1?e.map((function(e){var t=e;return t.id===parseInt("".concat(a.choiceId),10)&&(t.selected=!1),t})):e;case"FILTER_CHOICES":var c=n;return e.map((function(e){var t=e;return t.active=c.results.some((function(e){var i=e.item,n=e.score;return i.id===t.id&&(t.score=n,!0)})),t}));case"ACTIVATE_CHOICES":var l=n;return e.map((function(e){var t=e;return t.active=l.active,t}));case"CLEAR_CHOICES":return t.defaultState;default:return e}}},871:function(e,t){var i=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r<s;r++)!n&&r in t||(n||(n=Array.prototype.slice.call(t,0,r)),n[r]=t[r]);return e.concat(n||Array.prototype.slice.call(t))};Object.defineProperty(t,"__esModule",{value:!0}),t.defaultState=void 0,t.defaultState=[],t.default=function(e,n){switch(void 0===e&&(e=t.defaultState),void 0===n&&(n={}),n.type){case"ADD_GROUP":var r=n;return i(i([],e,!0),[{id:r.id,value:r.value,active:r.active,disabled:r.disabled}],!1);case"CLEAR_CHOICES":return[];default:return e}}},655:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.defaultState=void 0;var r=i(791),s=n(i(52)),o=n(i(871)),a=n(i(273)),c=n(i(502)),l=i(799);t.defaultState={groups:[],items:[],choices:[],loading:!1};var h=(0,r.combineReducers)({items:s.default,groups:o.default,choices:a.default,loading:c.default});t.default=function(e,i){var n=e;if("CLEAR_ALL"===i.type)n=t.defaultState;else if("RESET_TO"===i.type)return(0,l.cloneObject)(i.state);return h(n,i)}},52:function(e,t){var i=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r<s;r++)!n&&r in t||(n||(n=Array.prototype.slice.call(t,0,r)),n[r]=t[r]);return e.concat(n||Array.prototype.slice.call(t))};Object.defineProperty(t,"__esModule",{value:!0}),t.defaultState=void 0,t.defaultState=[],t.default=function(e,n){switch(void 0===e&&(e=t.defaultState),void 0===n&&(n={}),n.type){case"ADD_ITEM":var r=n;return i(i([],e,!0),[{id:r.id,choiceId:r.choiceId,groupId:r.groupId,value:r.value,label:r.label,active:!0,highlighted:!1,customProperties:r.customProperties,placeholder:r.placeholder||!1,keyCode:null}],!1).map((function(e){var t=e;return t.highlighted=!1,t}));case"REMOVE_ITEM":return e.map((function(e){var t=e;return t.id===n.id&&(t.active=!1),t}));case"HIGHLIGHT_ITEM":var s=n;return e.map((function(e){var t=e;return t.id===s.id&&(t.highlighted=s.highlighted),t}));default:return e}}},502:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.defaultState=void 0,t.defaultState=!1,t.default=function(e,i){return void 0===e&&(e=t.defaultState),void 0===i&&(i={}),"SET_IS_LOADING"===i.type?i.isLoading:e}},744:function(e,t,i){var n=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r<s;r++)!n&&r in t||(n||(n=Array.prototype.slice.call(t,0,r)),n[r]=t[r]);return e.concat(n||Array.prototype.slice.call(t))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var s=i(791),o=r(i(655)),a=function(){function e(){this._store=(0,s.createStore)(o.default,window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__())}return e.prototype.subscribe=function(e){this._store.subscribe(e)},e.prototype.dispatch=function(e){this._store.dispatch(e)},Object.defineProperty(e.prototype,"state",{get:function(){return this._store.getState()},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"items",{get:function(){return this.state.items},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeItems",{get:function(){return this.items.filter((function(e){return!0===e.active}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"highlightedActiveItems",{get:function(){return this.items.filter((function(e){return e.active&&e.highlighted}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"choices",{get:function(){return this.state.choices},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeChoices",{get:function(){return this.choices.filter((function(e){return!0===e.active}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"selectableChoices",{get:function(){return this.choices.filter((function(e){return!0!==e.disabled}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"searchableChoices",{get:function(){return this.selectableChoices.filter((function(e){return!0!==e.placeholder}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"placeholderChoice",{get:function(){return n([],this.choices,!0).reverse().find((function(e){return!0===e.placeholder}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"groups",{get:function(){return this.state.groups},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeGroups",{get:function(){var e=this.groups,t=this.choices;return e.filter((function(e){var i=!0===e.active&&!1===e.disabled,n=t.some((function(e){return!0===e.active&&!1===e.disabled}));return i&&n}),[])},enumerable:!1,configurable:!0}),e.prototype.isLoading=function(){return this.state.loading},e.prototype.getChoiceById=function(e){return this.activeChoices.find((function(t){return t.id===parseInt(e,10)}))},e.prototype.getGroupById=function(e){return this.groups.find((function(t){return t.id===e}))},e}();t.default=a},686:function(e,t){Object.defineProperty(t,"__esModule",{value:!0});var i={containerOuter:function(e,t,i,n,r,s,o){var a=e.classNames.containerOuter,c=Object.assign(document.createElement("div"),{className:a});return c.dataset.type=s,t&&(c.dir=t),n&&(c.tabIndex=0),i&&(c.setAttribute("role",r?"combobox":"listbox"),r&&c.setAttribute("aria-autocomplete","list")),c.setAttribute("aria-haspopup","true"),c.setAttribute("aria-expanded","false"),o&&c.setAttribute("aria-labelledby",o),c},containerInner:function(e){var t=e.classNames.containerInner;return Object.assign(document.createElement("div"),{className:t})},itemList:function(e,t){var i=e.classNames,n=i.list,r=i.listSingle,s=i.listItems;return Object.assign(document.createElement("div"),{className:"".concat(n," ").concat(t?r:s)})},placeholder:function(e,t){var i,n=e.allowHTML,r=e.classNames.placeholder;return Object.assign(document.createElement("div"),((i={className:r})[n?"innerHTML":"innerText"]=t,i))},item:function(e,t,i){var n,r,s=e.allowHTML,o=e.classNames,a=o.item,c=o.button,l=o.highlightedState,h=o.itemSelectable,u=o.placeholder,d=t.id,p=t.value,f=t.label,m=t.customProperties,v=t.active,g=t.disabled,_=t.highlighted,y=t.placeholder,E=Object.assign(document.createElement("div"),((n={className:a})[s?"innerHTML":"innerText"]=f,n));if(Object.assign(E.dataset,{item:"",id:d,value:p,customProperties:m}),v&&E.setAttribute("aria-selected","true"),g&&E.setAttribute("aria-disabled","true"),y&&E.classList.add(u),E.classList.add(_?l:h),i){g&&E.classList.remove(h),E.dataset.deletable="";var b="Remove item",S=Object.assign(document.createElement("button"),((r={type:"button",className:c})[s?"innerHTML":"innerText"]=b,r));S.setAttribute("aria-label","".concat(b,": '").concat(p,"'")),S.dataset.button="",E.appendChild(S)}return E},choiceList:function(e,t){var i=e.classNames.list,n=Object.assign(document.createElement("div"),{className:i});return t||n.setAttribute("aria-multiselectable","true"),n.setAttribute("role","listbox"),n},choiceGroup:function(e,t){var i,n=e.allowHTML,r=e.classNames,s=r.group,o=r.groupHeading,a=r.itemDisabled,c=t.id,l=t.value,h=t.disabled,u=Object.assign(document.createElement("div"),{className:"".concat(s," ").concat(h?a:"")});return u.setAttribute("role","group"),Object.assign(u.dataset,{group:"",id:c,value:l}),h&&u.setAttribute("aria-disabled","true"),u.appendChild(Object.assign(document.createElement("div"),((i={className:o})[n?"innerHTML":"innerText"]=l,i))),u},choice:function(e,t,i){var n,r=e.allowHTML,s=e.classNames,o=s.item,a=s.itemChoice,c=s.itemSelectable,l=s.selectedState,h=s.itemDisabled,u=s.placeholder,d=t.id,p=t.value,f=t.label,m=t.groupId,v=t.elementId,g=t.disabled,_=t.selected,y=t.placeholder,E=Object.assign(document.createElement("div"),((n={id:v})[r?"innerHTML":"innerText"]=f,n.className="".concat(o," ").concat(a),n));return _&&E.classList.add(l),y&&E.classList.add(u),E.setAttribute("role",m&&m>0?"treeitem":"option"),Object.assign(E.dataset,{choice:"",id:d,value:p,selectText:i}),g?(E.classList.add(h),E.dataset.choiceDisabled="",E.setAttribute("aria-disabled","true")):(E.classList.add(c),E.dataset.choiceSelectable=""),E},input:function(e,t){var i=e.classNames,n=i.input,r=i.inputCloned,s=Object.assign(document.createElement("input"),{type:"search",name:"search_terms",className:"".concat(n," ").concat(r),autocomplete:"off",autocapitalize:"off",spellcheck:!1});return s.setAttribute("role","textbox"),s.setAttribute("aria-autocomplete","list"),s.setAttribute("aria-label",t),s},dropdown:function(e){var t=e.classNames,i=t.list,n=t.listDropdown,r=document.createElement("div");return r.classList.add(i,n),r.setAttribute("aria-expanded","false"),r},notice:function(e,t,i){var n,r=e.allowHTML,s=e.classNames,o=s.item,a=s.itemChoice,c=s.noResults,l=s.noChoices;void 0===i&&(i="");var h=[o,a];return"no-choices"===i?h.push(l):"no-results"===i&&h.push(c),Object.assign(document.createElement("div"),((n={})[r?"innerHTML":"innerText"]=t,n.className=h.join(" "),n))},option:function(e){var t=e.label,i=e.value,n=e.customProperties,r=e.active,s=e.disabled,o=new Option(t,i,!1,r);return n&&(o.dataset.customProperties="".concat(n)),o.disabled=!!s,o}};t.default=i},996:function(e){var t=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===i}(e)}(e)},i="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function n(e,t){return!1!==t.clone&&t.isMergeableObject(e)?a((i=e,Array.isArray(i)?[]:{}),e,t):e;var i}function r(e,t,i){return e.concat(t).map((function(e){return n(e,i)}))}function s(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return e.propertyIsEnumerable(t)})):[]}(e))}function o(e,t){try{return t in e}catch(e){return!1}}function a(e,i,c){(c=c||{}).arrayMerge=c.arrayMerge||r,c.isMergeableObject=c.isMergeableObject||t,c.cloneUnlessOtherwiseSpecified=n;var l=Array.isArray(i);return l===Array.isArray(e)?l?c.arrayMerge(e,i,c):function(e,t,i){var r={};return i.isMergeableObject(e)&&s(e).forEach((function(t){r[t]=n(e[t],i)})),s(t).forEach((function(s){(function(e,t){return o(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,s)||(o(e,s)&&i.isMergeableObject(t[s])?r[s]=function(e,t){if(!t.customMerge)return a;var i=t.customMerge(e);return"function"==typeof i?i:a}(s,i)(e[s],t[s],i):r[s]=n(t[s],i))})),r}(e,i,c):n(i,c)}a.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,i){return a(e,i,t)}),{})};var c=a;e.exports=c},221:function(e,t,i){function n(e){return Array.isArray?Array.isArray(e):"[object Array]"===l(e)}function r(e){return"string"==typeof e}function s(e){return"number"==typeof e}function o(e){return"object"==typeof e}function a(e){return null!=e}function c(e){return!e.trim().length}function l(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":Object.prototype.toString.call(e)}i.r(t),i.d(t,{default:function(){return R}});const h=Object.prototype.hasOwnProperty;class u{constructor(e){this._keys=[],this._keyMap={};let t=0;e.forEach((e=>{let i=d(e);t+=i.weight,this._keys.push(i),this._keyMap[i.id]=i,t+=i.weight})),this._keys.forEach((e=>{e.weight/=t}))}get(e){return this._keyMap[e]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function d(e){let t=null,i=null,s=null,o=1,a=null;if(r(e)||n(e))s=e,t=p(e),i=f(e);else{if(!h.call(e,"name"))throw new Error("Missing name property in key");const n=e.name;if(s=n,h.call(e,"weight")&&(o=e.weight,o<=0))throw new Error((e=>`Property 'weight' in key '${e}' must be a positive integer`)(n));t=p(n),i=f(n),a=e.getFn}return{path:t,id:i,weight:o,src:s,getFn:a}}function p(e){return n(e)?e:e.split(".")}function f(e){return n(e)?e.join("."):e}var m={isCaseSensitive:!1,includeScore:!1,keys:[],shouldSort:!0,sortFn:(e,t)=>e.score===t.score?e.idx<t.idx?-1:1:e.score<t.score?-1:1,includeMatches:!1,findAllMatches:!1,minMatchCharLength:1,location:0,threshold:.6,distance:100,useExtendedSearch:!1,getFn:function(e,t){let i=[],c=!1;const h=(e,t,u)=>{if(a(e))if(t[u]){const d=e[t[u]];if(!a(d))return;if(u===t.length-1&&(r(d)||s(d)||function(e){return!0===e||!1===e||function(e){return o(e)&&null!==e}(e)&&"[object Boolean]"==l(e)}(d)))i.push(function(e){return null==e?"":function(e){if("string"==typeof e)return e;let t=e+"";return"0"==t&&1/e==-1/0?"-0":t}(e)}(d));else if(n(d)){c=!0;for(let e=0,i=d.length;e<i;e+=1)h(d[e],t,u+1)}else t.length&&h(d,t,u+1)}else i.push(e)};return h(e,r(t)?t.split("."):t,0),c?i:i[0]},ignoreLocation:!1,ignoreFieldNorm:!1,fieldNormWeight:1};const v=/[^ ]+/g;class g{constructor({getFn:e=m.getFn,fieldNormWeight:t=m.fieldNormWeight}={}){this.norm=function(e=1,t=3){const i=new Map,n=Math.pow(10,t);return{get(t){const r=t.match(v).length;if(i.has(r))return i.get(r);const s=1/Math.pow(r,.5*e),o=parseFloat(Math.round(s*n)/n);return i.set(r,o),o},clear(){i.clear()}}}(t,3),this.getFn=e,this.isCreated=!1,this.setIndexRecords()}setSources(e=[]){this.docs=e}setIndexRecords(e=[]){this.records=e}setKeys(e=[]){this.keys=e,this._keysMap={},e.forEach(((e,t)=>{this._keysMap[e.id]=t}))}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,r(this.docs[0])?this.docs.forEach(((e,t)=>{this._addString(e,t)})):this.docs.forEach(((e,t)=>{this._addObject(e,t)})),this.norm.clear())}add(e){const t=this.size();r(e)?this._addString(e,t):this._addObject(e,t)}removeAt(e){this.records.splice(e,1);for(let t=e,i=this.size();t<i;t+=1)this.records[t].i-=1}getValueForItemAtKeyId(e,t){return e[this._keysMap[t]]}size(){return this.records.length}_addString(e,t){if(!a(e)||c(e))return;let i={v:e,i:t,n:this.norm.get(e)};this.records.push(i)}_addObject(e,t){let i={i:t,$:{}};this.keys.forEach(((t,s)=>{let o=t.getFn?t.getFn(e):this.getFn(e,t.path);if(a(o))if(n(o)){let e=[];const t=[{nestedArrIndex:-1,value:o}];for(;t.length;){const{nestedArrIndex:i,value:s}=t.pop();if(a(s))if(r(s)&&!c(s)){let t={v:s,i:i,n:this.norm.get(s)};e.push(t)}else n(s)&&s.forEach(((e,i)=>{t.push({nestedArrIndex:i,value:e})}))}i.$[s]=e}else if(r(o)&&!c(o)){let e={v:o,n:this.norm.get(o)};i.$[s]=e}})),this.records.push(i)}toJSON(){return{keys:this.keys,records:this.records}}}function _(e,t,{getFn:i=m.getFn,fieldNormWeight:n=m.fieldNormWeight}={}){const r=new g({getFn:i,fieldNormWeight:n});return r.setKeys(e.map(d)),r.setSources(t),r.create(),r}function y(e,{errors:t=0,currentLocation:i=0,expectedLocation:n=0,distance:r=m.distance,ignoreLocation:s=m.ignoreLocation}={}){const o=t/e.length;if(s)return o;const a=Math.abs(n-i);return r?o+a/r:a?1:o}const E=32;function b(e){let t={};for(let i=0,n=e.length;i<n;i+=1){const r=e.charAt(i);t[r]=(t[r]||0)|1<<n-i-1}return t}class S{constructor(e,{location:t=m.location,threshold:i=m.threshold,distance:n=m.distance,includeMatches:r=m.includeMatches,findAllMatches:s=m.findAllMatches,minMatchCharLength:o=m.minMatchCharLength,isCaseSensitive:a=m.isCaseSensitive,ignoreLocation:c=m.ignoreLocation}={}){if(this.options={location:t,threshold:i,distance:n,includeMatches:r,findAllMatches:s,minMatchCharLength:o,isCaseSensitive:a,ignoreLocation:c},this.pattern=a?e:e.toLowerCase(),this.chunks=[],!this.pattern.length)return;const l=(e,t)=>{this.chunks.push({pattern:e,alphabet:b(e),startIndex:t})},h=this.pattern.length;if(h>E){let e=0;const t=h%E,i=h-t;for(;e<i;)l(this.pattern.substr(e,E),e),e+=E;if(t){const e=h-E;l(this.pattern.substr(e),e)}}else l(this.pattern,0)}searchIn(e){const{isCaseSensitive:t,includeMatches:i}=this.options;if(t||(e=e.toLowerCase()),this.pattern===e){let t={isMatch:!0,score:0};return i&&(t.indices=[[0,e.length-1]]),t}const{location:n,distance:r,threshold:s,findAllMatches:o,minMatchCharLength:a,ignoreLocation:c}=this.options;let l=[],h=0,u=!1;this.chunks.forEach((({pattern:t,alphabet:d,startIndex:p})=>{const{isMatch:f,score:v,indices:g}=function(e,t,i,{location:n=m.location,distance:r=m.distance,threshold:s=m.threshold,findAllMatches:o=m.findAllMatches,minMatchCharLength:a=m.minMatchCharLength,includeMatches:c=m.includeMatches,ignoreLocation:l=m.ignoreLocation}={}){if(t.length>E)throw new Error("Pattern length exceeds max of 32.");const h=t.length,u=e.length,d=Math.max(0,Math.min(n,u));let p=s,f=d;const v=a>1||c,g=v?Array(u):[];let _;for(;(_=e.indexOf(t,f))>-1;){let e=y(t,{currentLocation:_,expectedLocation:d,distance:r,ignoreLocation:l});if(p=Math.min(e,p),f=_+h,v){let e=0;for(;e<h;)g[_+e]=1,e+=1}}f=-1;let b=[],S=1,O=h+u;const I=1<<h-1;for(let n=0;n<h;n+=1){let s=0,a=O;for(;s<a;)y(t,{errors:n,currentLocation:d+a,expectedLocation:d,distance:r,ignoreLocation:l})<=p?s=a:O=a,a=Math.floor((O-s)/2+s);O=a;let c=Math.max(1,d-a+1),m=o?u:Math.min(d+a,u)+h,_=Array(m+2);_[m+1]=(1<<n)-1;for(let s=m;s>=c;s-=1){let o=s-1,a=i[e.charAt(o)];if(v&&(g[o]=+!!a),_[s]=(_[s+1]<<1|1)&a,n&&(_[s]|=(b[s+1]|b[s])<<1|1|b[s+1]),_[s]&I&&(S=y(t,{errors:n,currentLocation:o,expectedLocation:d,distance:r,ignoreLocation:l}),S<=p)){if(p=S,f=o,f<=d)break;c=Math.max(1,2*d-f)}}if(y(t,{errors:n+1,currentLocation:d,expectedLocation:d,distance:r,ignoreLocation:l})>p)break;b=_}const C={isMatch:f>=0,score:Math.max(.001,S)};if(v){const e=function(e=[],t=m.minMatchCharLength){let i=[],n=-1,r=-1,s=0;for(let o=e.length;s<o;s+=1){let o=e[s];o&&-1===n?n=s:o||-1===n||(r=s-1,r-n+1>=t&&i.push([n,r]),n=-1)}return e[s-1]&&s-n>=t&&i.push([n,s-1]),i}(g,a);e.length?c&&(C.indices=e):C.isMatch=!1}return C}(e,t,d,{location:n+p,distance:r,threshold:s,findAllMatches:o,minMatchCharLength:a,includeMatches:i,ignoreLocation:c});f&&(u=!0),h+=v,f&&g&&(l=[...l,...g])}));let d={isMatch:u,score:u?h/this.chunks.length:1};return u&&i&&(d.indices=l),d}}class O{constructor(e){this.pattern=e}static isMultiMatch(e){return I(e,this.multiRegex)}static isSingleMatch(e){return I(e,this.singleRegex)}search(){}}function I(e,t){const i=e.match(t);return i?i[1]:null}class C extends O{constructor(e,{location:t=m.location,threshold:i=m.threshold,distance:n=m.distance,includeMatches:r=m.includeMatches,findAllMatches:s=m.findAllMatches,minMatchCharLength:o=m.minMatchCharLength,isCaseSensitive:a=m.isCaseSensitive,ignoreLocation:c=m.ignoreLocation}={}){super(e),this._bitapSearch=new S(e,{location:t,threshold:i,distance:n,includeMatches:r,findAllMatches:s,minMatchCharLength:o,isCaseSensitive:a,ignoreLocation:c})}static get type(){return"fuzzy"}static get multiRegex(){return/^"(.*)"$/}static get singleRegex(){return/^(.*)$/}search(e){return this._bitapSearch.searchIn(e)}}class T extends O{constructor(e){super(e)}static get type(){return"include"}static get multiRegex(){return/^'"(.*)"$/}static get singleRegex(){return/^'(.*)$/}search(e){let t,i=0;const n=[],r=this.pattern.length;for(;(t=e.indexOf(this.pattern,i))>-1;)i=t+r,n.push([t,i-1]);const s=!!n.length;return{isMatch:s,score:s?0:1,indices:n}}}const L=[class extends O{constructor(e){super(e)}static get type(){return"exact"}static get multiRegex(){return/^="(.*)"$/}static get singleRegex(){return/^=(.*)$/}search(e){const t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},T,class extends O{constructor(e){super(e)}static get type(){return"prefix-exact"}static get multiRegex(){return/^\^"(.*)"$/}static get singleRegex(){return/^\^(.*)$/}search(e){const t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-prefix-exact"}static get multiRegex(){return/^!\^"(.*)"$/}static get singleRegex(){return/^!\^(.*)$/}search(e){const t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-suffix-exact"}static get multiRegex(){return/^!"(.*)"\$$/}static get singleRegex(){return/^!(.*)\$$/}search(e){const t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"suffix-exact"}static get multiRegex(){return/^"(.*)"\$$/}static get singleRegex(){return/^(.*)\$$/}search(e){const t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-exact"}static get multiRegex(){return/^!"(.*)"$/}static get singleRegex(){return/^!(.*)$/}search(e){const t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},C],w=L.length,A=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,M=new Set([C.type,T.type]);const P=[];function x(e,t){for(let i=0,n=P.length;i<n;i+=1){let n=P[i];if(n.condition(e,t))return new n(e,t)}return new S(e,t)}const N="$and",D=e=>!(!e.$and&&!e.$or),j=e=>({[N]:Object.keys(e).map((t=>({[t]:e[t]})))});function F(e,t,{auto:i=!0}={}){const s=e=>{let a=Object.keys(e);const c=(e=>!!e.$path)(e);if(!c&&a.length>1&&!D(e))return s(j(e));if((e=>!n(e)&&o(e)&&!D(e))(e)){const n=c?e.$path:a[0],s=c?e.$val:e[n];if(!r(s))throw new Error((e=>`Invalid value for key ${e}`)(n));const o={keyId:f(n),pattern:s};return i&&(o.searcher=x(s,t)),o}let l={children:[],operator:a[0]};return a.forEach((t=>{const i=e[t];n(i)&&i.forEach((e=>{l.children.push(s(e))}))})),l};return D(e)||(e=j(e)),s(e)}function k(e,t){const i=e.matches;t.matches=[],a(i)&&i.forEach((e=>{if(!a(e.indices)||!e.indices.length)return;const{indices:i,value:n}=e;let r={indices:i,value:n};e.key&&(r.key=e.key.src),e.idx>-1&&(r.refIndex=e.idx),t.matches.push(r)}))}function K(e,t){t.score=e.score}class R{constructor(e,t={},i){this.options={...m,...t},this.options.useExtendedSearch,this._keyStore=new u(this.options.keys),this.setCollection(e,i)}setCollection(e,t){if(this._docs=e,t&&!(t instanceof g))throw new Error("Incorrect 'index' type");this._myIndex=t||_(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}add(e){a(e)&&(this._docs.push(e),this._myIndex.add(e))}remove(e=(()=>!1)){const t=[];for(let i=0,n=this._docs.length;i<n;i+=1){const r=this._docs[i];e(r,i)&&(this.removeAt(i),i-=1,n-=1,t.push(r))}return t}removeAt(e){this._docs.splice(e,1),this._myIndex.removeAt(e)}getIndex(){return this._myIndex}search(e,{limit:t=-1}={}){const{includeMatches:i,includeScore:n,shouldSort:o,sortFn:a,ignoreFieldNorm:c}=this.options;let l=r(e)?r(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return function(e,{ignoreFieldNorm:t=m.ignoreFieldNorm}){e.forEach((e=>{let i=1;e.matches.forEach((({key:e,norm:n,score:r})=>{const s=e?e.weight:null;i*=Math.pow(0===r&&s?Number.EPSILON:r,(s||1)*(t?1:n))})),e.score=i}))}(l,{ignoreFieldNorm:c}),o&&l.sort(a),s(t)&&t>-1&&(l=l.slice(0,t)),function(e,t,{includeMatches:i=m.includeMatches,includeScore:n=m.includeScore}={}){const r=[];return i&&r.push(k),n&&r.push(K),e.map((e=>{const{idx:i}=e,n={item:t[i],refIndex:i};return r.length&&r.forEach((t=>{t(e,n)})),n}))}(l,this._docs,{includeMatches:i,includeScore:n})}_searchStringList(e){const t=x(e,this.options),{records:i}=this._myIndex,n=[];return i.forEach((({v:e,i:i,n:r})=>{if(!a(e))return;const{isMatch:s,score:o,indices:c}=t.searchIn(e);s&&n.push({item:e,idx:i,matches:[{score:o,value:e,norm:r,indices:c}]})})),n}_searchLogical(e){const t=F(e,this.options),i=(e,t,n)=>{if(!e.children){const{keyId:i,searcher:r}=e,s=this._findMatches({key:this._keyStore.get(i),value:this._myIndex.getValueForItemAtKeyId(t,i),searcher:r});return s&&s.length?[{idx:n,item:t,matches:s}]:[]}const r=[];for(let s=0,o=e.children.length;s<o;s+=1){const o=e.children[s],a=i(o,t,n);if(a.length)r.push(...a);else if(e.operator===N)return[]}return r},n=this._myIndex.records,r={},s=[];return n.forEach((({$:e,i:n})=>{if(a(e)){let o=i(t,e,n);o.length&&(r[n]||(r[n]={idx:n,item:e,matches:[]},s.push(r[n])),o.forEach((({matches:e})=>{r[n].matches.push(...e)})))}})),s}_searchObjectList(e){const t=x(e,this.options),{keys:i,records:n}=this._myIndex,r=[];return n.forEach((({$:e,i:n})=>{if(!a(e))return;let s=[];i.forEach(((i,n)=>{s.push(...this._findMatches({key:i,value:e[n],searcher:t}))})),s.length&&r.push({idx:n,item:e,matches:s})})),r}_findMatches({key:e,value:t,searcher:i}){if(!a(t))return[];let r=[];if(n(t))t.forEach((({v:t,i:n,n:s})=>{if(!a(t))return;const{isMatch:o,score:c,indices:l}=i.searchIn(t);o&&r.push({score:c,key:e,value:t,idx:n,norm:s,indices:l})}));else{const{v:n,n:s}=t,{isMatch:o,score:a,indices:c}=i.searchIn(n);o&&r.push({score:a,key:e,value:n,norm:s,indices:c})}return r}}R.version="6.6.2",R.createIndex=_,R.parseIndex=function(e,{getFn:t=m.getFn,fieldNormWeight:i=m.fieldNormWeight}={}){const{keys:n,records:r}=e,s=new g({getFn:t,fieldNormWeight:i});return s.setKeys(n),s.setIndexRecords(r),s},R.config=m,R.parseQuery=F,function(...e){P.push(...e)}(class{constructor(e,{isCaseSensitive:t=m.isCaseSensitive,includeMatches:i=m.includeMatches,minMatchCharLength:n=m.minMatchCharLength,ignoreLocation:r=m.ignoreLocation,findAllMatches:s=m.findAllMatches,location:o=m.location,threshold:a=m.threshold,distance:c=m.distance}={}){this.query=null,this.options={isCaseSensitive:t,includeMatches:i,minMatchCharLength:n,findAllMatches:s,ignoreLocation:r,location:o,threshold:a,distance:c},this.pattern=t?e:e.toLowerCase(),this.query=function(e,t={}){return e.split("|").map((e=>{let i=e.trim().split(A).filter((e=>e&&!!e.trim())),n=[];for(let e=0,r=i.length;e<r;e+=1){const r=i[e];let s=!1,o=-1;for(;!s&&++o<w;){const e=L[o];let i=e.isMultiMatch(r);i&&(n.push(new e(i,t)),s=!0)}if(!s)for(o=-1;++o<w;){const e=L[o];let i=e.isSingleMatch(r);if(i){n.push(new e(i,t));break}}}return n}))}(this.pattern,this.options)}static condition(e,t){return t.useExtendedSearch}searchIn(e){const t=this.query;if(!t)return{isMatch:!1,score:1};const{includeMatches:i,isCaseSensitive:n}=this.options;e=n?e:e.toLowerCase();let r=0,s=[],o=0;for(let n=0,a=t.length;n<a;n+=1){const a=t[n];s.length=0,r=0;for(let t=0,n=a.length;t<n;t+=1){const n=a[t],{isMatch:c,indices:l,score:h}=n.search(e);if(!c){o=0,r=0,s.length=0;break}if(r+=1,o+=h,i){const e=n.constructor.type;M.has(e)?s=[...s,...l]:s.push(l)}}if(r){let e={isMatch:!0,score:o/r};return i&&(e.indices=s),e}}return{isMatch:!1,score:1}}})},791:function(e,t,i){function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function r(e,t,i){return(t=function(e){var t=function(e,t){if("object"!==n(e)||null===e)return e;var i=e[Symbol.toPrimitive];if(void 0!==i){var r=i.call(e,t);if("object"!==n(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e,"string");return"symbol"===n(t)?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function s(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,n)}return i}function o(e){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?s(Object(i),!0).forEach((function(t){r(e,t,i[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):s(Object(i)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))}))}return e}function a(e){return"Minified Redux error #"+e+"; visit https://redux.js.org/Errors?code="+e+" for the full message or use the non-minified dev environment for full errors. "}i.r(t),i.d(t,{__DO_NOT_USE__ActionTypes:function(){return h},applyMiddleware:function(){return _},bindActionCreators:function(){return v},combineReducers:function(){return f},compose:function(){return g},createStore:function(){return d},legacy_createStore:function(){return p}});var c="function"==typeof Symbol&&Symbol.observable||"@@observable",l=function(){return Math.random().toString(36).substring(7).split("").join(".")},h={INIT:"@@redux/INIT"+l(),REPLACE:"@@redux/REPLACE"+l(),PROBE_UNKNOWN_ACTION:function(){return"@@redux/PROBE_UNKNOWN_ACTION"+l()}};function u(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function d(e,t,i){var n;if("function"==typeof t&&"function"==typeof i||"function"==typeof i&&"function"==typeof arguments[3])throw new Error(a(0));if("function"==typeof t&&void 0===i&&(i=t,t=void 0),void 0!==i){if("function"!=typeof i)throw new Error(a(1));return i(d)(e,t)}if("function"!=typeof e)throw new Error(a(2));var r=e,s=t,o=[],l=o,p=!1;function f(){l===o&&(l=o.slice())}function m(){if(p)throw new Error(a(3));return s}function v(e){if("function"!=typeof e)throw new Error(a(4));if(p)throw new Error(a(5));var t=!0;return f(),l.push(e),function(){if(t){if(p)throw new Error(a(6));t=!1,f();var i=l.indexOf(e);l.splice(i,1),o=null}}}function g(e){if(!u(e))throw new Error(a(7));if(void 0===e.type)throw new Error(a(8));if(p)throw new Error(a(9));try{p=!0,s=r(s,e)}finally{p=!1}for(var t=o=l,i=0;i<t.length;i++)(0,t[i])();return e}function _(e){if("function"!=typeof e)throw new Error(a(10));r=e,g({type:h.REPLACE})}function y(){var e,t=v;return(e={subscribe:function(e){if("object"!=typeof e||null===e)throw new Error(a(11));function i(){e.next&&e.next(m())}return i(),{unsubscribe:t(i)}}})[c]=function(){return this},e}return g({type:h.INIT}),(n={dispatch:g,subscribe:v,getState:m,replaceReducer:_})[c]=y,n}var p=d;function f(e){for(var t=Object.keys(e),i={},n=0;n<t.length;n++){var r=t[n];"function"==typeof e[r]&&(i[r]=e[r])}var s,o=Object.keys(i);try{!function(e){Object.keys(e).forEach((function(t){var i=e[t];if(void 0===i(void 0,{type:h.INIT}))throw new Error(a(12));if(void 0===i(void 0,{type:h.PROBE_UNKNOWN_ACTION()}))throw new Error(a(13))}))}(i)}catch(e){s=e}return function(e,t){if(void 0===e&&(e={}),s)throw s;for(var n=!1,r={},c=0;c<o.length;c++){var l=o[c],h=i[l],u=e[l],d=h(u,t);if(void 0===d)throw t&&t.type,new Error(a(14));r[l]=d,n=n||d!==u}return(n=n||o.length!==Object.keys(e).length)?r:e}}function m(e,t){return function(){return t(e.apply(this,arguments))}}function v(e,t){if("function"==typeof e)return m(e,t);if("object"!=typeof e||null===e)throw new Error(a(16));var i={};for(var n in e){var r=e[n];"function"==typeof r&&(i[n]=m(r,t))}return i}function g(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];return 0===t.length?function(e){return e}:1===t.length?t[0]:t.reduce((function(e,t){return function(){return e(t.apply(void 0,arguments))}}))}function _(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];return function(e){return function(){var i=e.apply(void 0,arguments),n=function(){throw new Error(a(15))},r={getState:i.getState,dispatch:function(){return n.apply(void 0,arguments)}},s=t.map((function(e){return e(r)}));return n=g.apply(void 0,s)(i.dispatch),o(o({},i),{},{dispatch:n})}}}}},t={};function i(n){var r=t[n];if(void 0!==r)return r.exports;var s=t[n]={exports:{}};return e[n].call(s.exports,s,s.exports,i),s.exports}i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,{a:t}),t},i.d=function(e,t){for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var n,r,s={};n=i(373),r=i.n(n),i(187),i(883),i(789),i(686),s.default=r(),window.Choices=s.default}();
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/assets/scripts/choices.min.js.LICENSE.txt b/my_flask_app/static/choices.js/public/assets/scripts/choices.min.js.LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c68d67078e69031a3e93577d555829423169861d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/scripts/choices.min.js.LICENSE.txt
@@ -0,0 +1 @@
+/*! choices.js v10.2.0 | © 2022 Josh Johnson | https://github.com/jshjohnson/Choices#readme */
diff --git a/my_flask_app/static/choices.js/public/assets/styles/base.css b/my_flask_app/static/choices.js/public/assets/styles/base.css
new file mode 100644
index 0000000000000000000000000000000000000000..b64f6b95323c1336d630126982eda30de3044c30
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/styles/base.css
@@ -0,0 +1,181 @@
+/* =============================================
+=            Generic styling                  =
+============================================= */
+* {
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+}
+
+html,
+body {
+  position: relative;
+  margin: 0;
+  width: 100%;
+  height: 100%;
+}
+
+body {
+  font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
+  font-size: 16px;
+  line-height: 1.4;
+  color: #fff;
+  background-color: #333;
+  overflow-x: hidden;
+}
+
+label {
+  display: block;
+  margin-bottom: 8px;
+  font-size: 14px;
+  font-weight: 500;
+  cursor: pointer;
+}
+
+p {
+  margin-top: 0;
+  margin-bottom: 8px;
+}
+
+hr {
+  display: block;
+  margin: 30px 0;
+  border: 0;
+  border-bottom: 1px solid #eaeaea;
+  height: 1px;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  margin-top: 0;
+  margin-bottom: 12px;
+  font-weight: 400;
+  line-height: 1.2;
+}
+
+a,
+a:visited,
+a:focus {
+  color: #fff;
+  text-decoration: none;
+  font-weight: 600;
+}
+
+.form-control {
+  display: block;
+  width: 100%;
+  background-color: #f9f9f9;
+  padding: 12px;
+  border: 1px solid #ddd;
+  border-radius: 2.5px;
+  font-size: 14px;
+  -webkit-appearance: none;
+          appearance: none;
+  margin-bottom: 24px;
+}
+
+h1,
+.h1 {
+  font-size: 32px;
+}
+
+h2,
+.h2 {
+  font-size: 24px;
+}
+
+h3,
+.h3 {
+  font-size: 20px;
+}
+
+h4,
+.h4 {
+  font-size: 18px;
+}
+
+h5,
+.h5 {
+  font-size: 16px;
+}
+
+h6,
+.h6 {
+  font-size: 14px;
+}
+
+label + p {
+  margin-top: -4px;
+}
+
+.container {
+  display: block;
+  margin: auto;
+  max-width: 40em;
+  padding: 48px;
+}
+@media (max-width: 620px) {
+  .container {
+    padding: 0;
+  }
+}
+
+.section {
+  background-color: #fff;
+  padding: 24px;
+  color: #333;
+}
+.section a,
+.section a:visited,
+.section a:focus {
+  color: #00bcd4;
+}
+
+.logo {
+  display: block;
+  margin-bottom: 12px;
+}
+
+.logo-img {
+  width: 100%;
+  height: auto;
+  display: inline-block;
+  max-width: 100%;
+  vertical-align: top;
+  padding: 6px 0;
+}
+
+.visible-ie {
+  display: none;
+}
+
+.push-bottom {
+  margin-bottom: 24px;
+}
+
+.zero-bottom {
+  margin-bottom: 0;
+}
+
+.zero-top {
+  margin-top: 0;
+}
+
+.text-center {
+  text-align: center;
+}
+
+[data-test-hook] {
+  margin-bottom: 24px;
+}
+
+/* =====  End of Section comment block  ====== */
diff --git a/my_flask_app/static/choices.js/public/assets/styles/base.css.map b/my_flask_app/static/choices.js/public/assets/styles/base.css.map
new file mode 100644
index 0000000000000000000000000000000000000000..5ae6c14f5b51b4792e2194e8232e377f30f42e87
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/styles/base.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["../../../src/styles/base.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAYA;EACE;EACA;;;AAGF;AAAA;AAAA;EAGE;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,eAtFiB;;;AAyFnB;AAAA;EAEE,WA1FoB;;;AA6FtB;AAAA;EAEE,WA9FoB;;;AAiGtB;AAAA;EAEE,WAlGoB;;;AAqGtB;AAAA;EAEE,WAtGoB;;;AAyGtB;AAAA;EAEE,WA1GoB;;;AA6GtB;AAAA;EAEE,WA9GoB;;;AAiHtB;EACE;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EANF;IAOI;;;;AAIJ;EACE;EACA,SAxIiB;EAyIjB;;AAEA;AAAA;AAAA;EAGE;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE,eArKiB;;;AAwKnB;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,eArLiB;;;AAwLnB","file":"base.css"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/assets/styles/base.min.css b/my_flask_app/static/choices.js/public/assets/styles/base.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..53c15adb58d3067a870b32f94f3f3b717b7b89ef
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/styles/base.min.css
@@ -0,0 +1 @@
+*{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,::after,::before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label,p{margin-bottom:8px}label{font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:30px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;-webkit-appearance:none;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}label+p{margin-top:-4px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#00bcd4}.logo{display:block;margin-bottom:12px}.logo-img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}.push-bottom{margin-bottom:24px}.zero-bottom{margin-bottom:0}.zero-top{margin-top:0}.text-center{text-align:center}[data-test-hook]{margin-bottom:24px}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/assets/styles/choices.css b/my_flask_app/static/choices.js/public/assets/styles/choices.css
new file mode 100644
index 0000000000000000000000000000000000000000..ed8ff340e2a36e66e0eccde8a891d79e4160befe
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/styles/choices.css
@@ -0,0 +1,340 @@
+/* ===============================
+=            Choices            =
+=============================== */
+.choices {
+  position: relative;
+  overflow: hidden;
+  margin-bottom: 24px;
+  font-size: 16px;
+}
+.choices:focus {
+  outline: none;
+}
+.choices:last-child {
+  margin-bottom: 0;
+}
+.choices.is-open {
+  overflow: visible;
+}
+.choices.is-disabled .choices__inner,
+.choices.is-disabled .choices__input {
+  background-color: #eaeaea;
+  cursor: not-allowed;
+  -webkit-user-select: none;
+          user-select: none;
+}
+.choices.is-disabled .choices__item {
+  cursor: not-allowed;
+}
+.choices [hidden] {
+  display: none !important;
+}
+
+.choices[data-type*=select-one] {
+  cursor: pointer;
+}
+.choices[data-type*=select-one] .choices__inner {
+  padding-bottom: 7.5px;
+}
+.choices[data-type*=select-one] .choices__input {
+  display: block;
+  width: 100%;
+  padding: 10px;
+  border-bottom: 1px solid #ddd;
+  background-color: #fff;
+  margin: 0;
+}
+.choices[data-type*=select-one] .choices__button {
+  background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==");
+  padding: 0;
+  background-size: 8px;
+  position: absolute;
+  top: 50%;
+  right: 0;
+  margin-top: -10px;
+  margin-right: 25px;
+  height: 20px;
+  width: 20px;
+  border-radius: 10em;
+  opacity: 0.25;
+}
+.choices[data-type*=select-one] .choices__button:hover, .choices[data-type*=select-one] .choices__button:focus {
+  opacity: 1;
+}
+.choices[data-type*=select-one] .choices__button:focus {
+  box-shadow: 0 0 0 2px #00bcd4;
+}
+.choices[data-type*=select-one] .choices__item[data-value=""] .choices__button {
+  display: none;
+}
+.choices[data-type*=select-one]::after {
+  content: "";
+  height: 0;
+  width: 0;
+  border-style: solid;
+  border-color: #333 transparent transparent transparent;
+  border-width: 5px;
+  position: absolute;
+  right: 11.5px;
+  top: 50%;
+  margin-top: -2.5px;
+  pointer-events: none;
+}
+.choices[data-type*=select-one].is-open::after {
+  border-color: transparent transparent #333 transparent;
+  margin-top: -7.5px;
+}
+.choices[data-type*=select-one][dir=rtl]::after {
+  left: 11.5px;
+  right: auto;
+}
+.choices[data-type*=select-one][dir=rtl] .choices__button {
+  right: auto;
+  left: 0;
+  margin-left: 25px;
+  margin-right: 0;
+}
+
+.choices[data-type*=select-multiple] .choices__inner,
+.choices[data-type*=text] .choices__inner {
+  cursor: text;
+}
+.choices[data-type*=select-multiple] .choices__button,
+.choices[data-type*=text] .choices__button {
+  position: relative;
+  display: inline-block;
+  margin-top: 0;
+  margin-right: -4px;
+  margin-bottom: 0;
+  margin-left: 8px;
+  padding-left: 16px;
+  border-left: 1px solid #008fa1;
+  background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==");
+  background-size: 8px;
+  width: 8px;
+  line-height: 1;
+  opacity: 0.75;
+  border-radius: 0;
+}
+.choices[data-type*=select-multiple] .choices__button:hover, .choices[data-type*=select-multiple] .choices__button:focus,
+.choices[data-type*=text] .choices__button:hover,
+.choices[data-type*=text] .choices__button:focus {
+  opacity: 1;
+}
+
+.choices__inner {
+  display: inline-block;
+  vertical-align: top;
+  width: 100%;
+  background-color: #f9f9f9;
+  padding: 7.5px 7.5px 3.75px;
+  border: 1px solid #ddd;
+  border-radius: 2.5px;
+  font-size: 14px;
+  min-height: 44px;
+  overflow: hidden;
+}
+.is-focused .choices__inner, .is-open .choices__inner {
+  border-color: #b7b7b7;
+}
+.is-open .choices__inner {
+  border-radius: 2.5px 2.5px 0 0;
+}
+.is-flipped.is-open .choices__inner {
+  border-radius: 0 0 2.5px 2.5px;
+}
+
+.choices__list {
+  margin: 0;
+  padding-left: 0;
+  list-style: none;
+}
+.choices__list--single {
+  display: inline-block;
+  padding: 4px 16px 4px 4px;
+  width: 100%;
+}
+[dir=rtl] .choices__list--single {
+  padding-right: 4px;
+  padding-left: 16px;
+}
+.choices__list--single .choices__item {
+  width: 100%;
+}
+
+.choices__list--multiple {
+  display: inline;
+}
+.choices__list--multiple .choices__item {
+  display: inline-block;
+  vertical-align: middle;
+  border-radius: 20px;
+  padding: 4px 10px;
+  font-size: 12px;
+  font-weight: 500;
+  margin-right: 3.75px;
+  margin-bottom: 3.75px;
+  background-color: #00bcd4;
+  border: 1px solid #00a5bb;
+  color: #fff;
+  word-break: break-all;
+  box-sizing: border-box;
+}
+.choices__list--multiple .choices__item[data-deletable] {
+  padding-right: 5px;
+}
+[dir=rtl] .choices__list--multiple .choices__item {
+  margin-right: 0;
+  margin-left: 3.75px;
+}
+.choices__list--multiple .choices__item.is-highlighted {
+  background-color: #00a5bb;
+  border: 1px solid #008fa1;
+}
+.is-disabled .choices__list--multiple .choices__item {
+  background-color: #aaaaaa;
+  border: 1px solid #919191;
+}
+
+.choices__list--dropdown, .choices__list[aria-expanded] {
+  visibility: hidden;
+  z-index: 1;
+  position: absolute;
+  width: 100%;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  top: 100%;
+  margin-top: -1px;
+  border-bottom-left-radius: 2.5px;
+  border-bottom-right-radius: 2.5px;
+  overflow: hidden;
+  word-break: break-all;
+  will-change: visibility;
+}
+.is-active.choices__list--dropdown, .is-active.choices__list[aria-expanded] {
+  visibility: visible;
+}
+.is-open .choices__list--dropdown, .is-open .choices__list[aria-expanded] {
+  border-color: #b7b7b7;
+}
+.is-flipped .choices__list--dropdown, .is-flipped .choices__list[aria-expanded] {
+  top: auto;
+  bottom: 100%;
+  margin-top: 0;
+  margin-bottom: -1px;
+  border-radius: 0.25rem 0.25rem 0 0;
+}
+.choices__list--dropdown .choices__list, .choices__list[aria-expanded] .choices__list {
+  position: relative;
+  max-height: 300px;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  will-change: scroll-position;
+}
+.choices__list--dropdown .choices__item, .choices__list[aria-expanded] .choices__item {
+  position: relative;
+  padding: 10px;
+  font-size: 14px;
+}
+[dir=rtl] .choices__list--dropdown .choices__item, [dir=rtl] .choices__list[aria-expanded] .choices__item {
+  text-align: right;
+}
+@media (min-width: 640px) {
+  .choices__list--dropdown .choices__item--selectable, .choices__list[aria-expanded] .choices__item--selectable {
+    padding-right: 100px;
+  }
+  .choices__list--dropdown .choices__item--selectable::after, .choices__list[aria-expanded] .choices__item--selectable::after {
+    content: attr(data-select-text);
+    font-size: 12px;
+    opacity: 0;
+    position: absolute;
+    right: 10px;
+    top: 50%;
+    transform: translateY(-50%);
+  }
+  [dir=rtl] .choices__list--dropdown .choices__item--selectable, [dir=rtl] .choices__list[aria-expanded] .choices__item--selectable {
+    text-align: right;
+    padding-left: 100px;
+    padding-right: 10px;
+  }
+  [dir=rtl] .choices__list--dropdown .choices__item--selectable::after, [dir=rtl] .choices__list[aria-expanded] .choices__item--selectable::after {
+    right: auto;
+    left: 10px;
+  }
+}
+.choices__list--dropdown .choices__item--selectable.is-highlighted, .choices__list[aria-expanded] .choices__item--selectable.is-highlighted {
+  background-color: #f2f2f2;
+}
+.choices__list--dropdown .choices__item--selectable.is-highlighted::after, .choices__list[aria-expanded] .choices__item--selectable.is-highlighted::after {
+  opacity: 0.5;
+}
+
+.choices__item {
+  cursor: default;
+}
+
+.choices__item--selectable {
+  cursor: pointer;
+}
+
+.choices__item--disabled {
+  cursor: not-allowed;
+  -webkit-user-select: none;
+          user-select: none;
+  opacity: 0.5;
+}
+
+.choices__heading {
+  font-weight: 600;
+  font-size: 12px;
+  padding: 10px;
+  border-bottom: 1px solid #f7f7f7;
+  color: gray;
+}
+
+.choices__button {
+  text-indent: -9999px;
+  -webkit-appearance: none;
+          appearance: none;
+  border: 0;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  background-position: center;
+  cursor: pointer;
+}
+.choices__button:focus {
+  outline: none;
+}
+
+.choices__input {
+  display: inline-block;
+  vertical-align: baseline;
+  background-color: #f9f9f9;
+  font-size: 14px;
+  margin-bottom: 5px;
+  border: 0;
+  border-radius: 0;
+  max-width: 100%;
+  padding: 4px 0 4px 2px;
+}
+.choices__input:focus {
+  outline: 0;
+}
+.choices__input::-webkit-search-decoration, .choices__input::-webkit-search-cancel-button, .choices__input::-webkit-search-results-button, .choices__input::-webkit-search-results-decoration {
+  display: none;
+}
+.choices__input::-ms-clear, .choices__input::-ms-reveal {
+  display: none;
+  width: 0;
+  height: 0;
+}
+[dir=rtl] .choices__input {
+  padding-right: 2px;
+  padding-left: 0;
+}
+
+.choices__placeholder {
+  opacity: 0.5;
+}
+
+/* =====  End of Choices  ====== */
diff --git a/my_flask_app/static/choices.js/public/assets/styles/choices.css.map b/my_flask_app/static/choices.js/public/assets/styles/choices.css.map
new file mode 100644
index 0000000000000000000000000000000000000000..b30c277364801e9464e10b1da35432011a30ab3f
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/styles/choices.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["../../../src/styles/choices.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AA2BA;EACE;EACA;EACA,eApBkB;EAqBlB,WAxBqB;;AA0BrB;EACE;;AAGF;EACE;;AAGF;EACE;;AAIA;AAAA;EAEE,kBAlCsB;EAmCtB;EACA;;AAEF;EACE;;AAIJ;EACE;;;AAIJ;EACE;;AACA;EACE;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEF;EACE,kBApDyB;EAqDzB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;;AAGJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAIA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;;;AAOJ;AAAA;EACE;;AAEF;AAAA;EACE;EACA;EACA;EACA;EACA;EACA,aA5HoB;EA6HpB;EACA;EACA,kBA9HiB;EA+HjB,iBAjIuB;EAkIvB,OAlIuB;EAmIvB;EACA;EACA;;AAEA;AAAA;AAAA;EAEE;;;AAKN;EACE;EACA;EACA;EACA,kBA1JiB;EA2JjB;EACA;EACA,eA/JsB;EAgKtB,WAnKqB;EAoKrB;EACA;;AAEA;EAEE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAOF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;;;AAIJ;EACE;;AACA;EACE;EACA;EACA,eA9MyB;EA+MzB;EACA,WAnNmB;EAoNnB;EACA;EACA;EACA,kBA9MoB;EA+MpB;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;EACE;EACA,SApOgB;EAqOhB;EACA;EACA,kBAjP0B;EAkP1B;EACA;EACA;EACA,2BAzPsB;EA0PtB,4BA1PsB;EA2PtB;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA,WA3RmB;;AA6RnB;EACE;;AAIF;EADF;IAEI;;EAEA;IACE;IACA,WAtSe;IAuSf;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAEA;IACE;IACA;;;AAKN;EACE;;AAEA;EACE;;;AAUR;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA,WAxVqB;EAyVrB;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA,kBA3WiB;EA4WjB,WAjXqB;EAkXrB;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EAIE;;AAGF;EAEE;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF","file":"choices.css"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/assets/styles/choices.min.css b/my_flask_app/static/choices.js/public/assets/styles/choices.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..9260536aa771b7c0bbf7f4d32d0c463811b348ae
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/assets/styles/choices.min.css
@@ -0,0 +1 @@
+.choices{position:relative;overflow:hidden;margin-bottom:24px;font-size:16px}.choices:focus{outline:0}.choices:last-child{margin-bottom:0}.choices.is-open{overflow:visible}.choices.is-disabled .choices__inner,.choices.is-disabled .choices__input{background-color:#eaeaea;cursor:not-allowed;-webkit-user-select:none;user-select:none}.choices.is-disabled .choices__item{cursor:not-allowed}.choices [hidden]{display:none!important}.choices[data-type*=select-one]{cursor:pointer}.choices[data-type*=select-one] .choices__inner{padding-bottom:7.5px}.choices[data-type*=select-one] .choices__input{display:block;width:100%;padding:10px;border-bottom:1px solid #ddd;background-color:#fff;margin:0}.choices[data-type*=select-one] .choices__button{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);padding:0;background-size:8px;position:absolute;top:50%;right:0;margin-top:-10px;margin-right:25px;height:20px;width:20px;border-radius:10em;opacity:.25}.choices[data-type*=select-one] .choices__button:focus,.choices[data-type*=select-one] .choices__button:hover{opacity:1}.choices[data-type*=select-one] .choices__button:focus{box-shadow:0 0 0 2px #00bcd4}.choices[data-type*=select-one] .choices__item[data-value=""] .choices__button{display:none}.choices[data-type*=select-one]::after{content:"";height:0;width:0;border-style:solid;border-color:#333 transparent transparent;border-width:5px;position:absolute;right:11.5px;top:50%;margin-top:-2.5px;pointer-events:none}.choices[data-type*=select-one].is-open::after{border-color:transparent transparent #333;margin-top:-7.5px}.choices[data-type*=select-one][dir=rtl]::after{left:11.5px;right:auto}.choices[data-type*=select-one][dir=rtl] .choices__button{right:auto;left:0;margin-left:25px;margin-right:0}.choices[data-type*=select-multiple] .choices__inner,.choices[data-type*=text] .choices__inner{cursor:text}.choices[data-type*=select-multiple] .choices__button,.choices[data-type*=text] .choices__button{position:relative;display:inline-block;margin:0-4px 0 8px;padding-left:16px;border-left:1px solid #008fa1;background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);background-size:8px;width:8px;line-height:1;opacity:.75;border-radius:0}.choices[data-type*=select-multiple] .choices__button:focus,.choices[data-type*=select-multiple] .choices__button:hover,.choices[data-type*=text] .choices__button:focus,.choices[data-type*=text] .choices__button:hover{opacity:1}.choices__inner{display:inline-block;vertical-align:top;width:100%;background-color:#f9f9f9;padding:7.5px 7.5px 3.75px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;min-height:44px;overflow:hidden}.is-focused .choices__inner,.is-open .choices__inner{border-color:#b7b7b7}.is-open .choices__inner{border-radius:2.5px 2.5px 0 0}.is-flipped.is-open .choices__inner{border-radius:0 0 2.5px 2.5px}.choices__list{margin:0;padding-left:0;list-style:none}.choices__list--single{display:inline-block;padding:4px 16px 4px 4px;width:100%}[dir=rtl] .choices__list--single{padding-right:4px;padding-left:16px}.choices__list--single .choices__item{width:100%}.choices__list--multiple{display:inline}.choices__list--multiple .choices__item{display:inline-block;vertical-align:middle;border-radius:20px;padding:4px 10px;font-size:12px;font-weight:500;margin-right:3.75px;margin-bottom:3.75px;background-color:#00bcd4;border:1px solid #00a5bb;color:#fff;word-break:break-all;box-sizing:border-box}.choices__list--multiple .choices__item[data-deletable]{padding-right:5px}[dir=rtl] .choices__list--multiple .choices__item{margin-right:0;margin-left:3.75px}.choices__list--multiple .choices__item.is-highlighted{background-color:#00a5bb;border:1px solid #008fa1}.is-disabled .choices__list--multiple .choices__item{background-color:#aaa;border:1px solid #919191}.choices__list--dropdown,.choices__list[aria-expanded]{visibility:hidden;z-index:1;position:absolute;width:100%;background-color:#fff;border:1px solid #ddd;top:100%;margin-top:-1px;border-bottom-left-radius:2.5px;border-bottom-right-radius:2.5px;overflow:hidden;word-break:break-all;will-change:visibility}.is-active.choices__list--dropdown,.is-active.choices__list[aria-expanded]{visibility:visible}.is-open .choices__list--dropdown,.is-open .choices__list[aria-expanded]{border-color:#b7b7b7}.is-flipped .choices__list--dropdown,.is-flipped .choices__list[aria-expanded]{top:auto;bottom:100%;margin-top:0;margin-bottom:-1px;border-radius:.25rem .25rem 0 0}.choices__list--dropdown .choices__list,.choices__list[aria-expanded] .choices__list{position:relative;max-height:300px;overflow:auto;-webkit-overflow-scrolling:touch;will-change:scroll-position}.choices__list--dropdown .choices__item,.choices__list[aria-expanded] .choices__item{position:relative;padding:10px;font-size:14px}[dir=rtl] .choices__list--dropdown .choices__item,[dir=rtl] .choices__list[aria-expanded] .choices__item{text-align:right}@media (min-width:640px){.choices__list--dropdown .choices__item--selectable,.choices__list[aria-expanded] .choices__item--selectable{padding-right:100px}.choices__list--dropdown .choices__item--selectable::after,.choices__list[aria-expanded] .choices__item--selectable::after{content:attr(data-select-text);font-size:12px;opacity:0;position:absolute;right:10px;top:50%;transform:translateY(-50%)}[dir=rtl] .choices__list--dropdown .choices__item--selectable,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable{text-align:right;padding-left:100px;padding-right:10px}[dir=rtl] .choices__list--dropdown .choices__item--selectable::after,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable::after{right:auto;left:10px}}.choices__list--dropdown .choices__item--selectable.is-highlighted,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted{background-color:#f2f2f2}.choices__list--dropdown .choices__item--selectable.is-highlighted::after,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted::after{opacity:.5}.choices__item{cursor:default}.choices__item--selectable{cursor:pointer}.choices__item--disabled{cursor:not-allowed;-webkit-user-select:none;user-select:none;opacity:.5}.choices__heading{font-weight:600;font-size:12px;padding:10px;border-bottom:1px solid #f7f7f7;color:gray}.choices__button{text-indent:-9999px;-webkit-appearance:none;appearance:none;border:0;background-color:transparent;background-repeat:no-repeat;background-position:center;cursor:pointer}.choices__button:focus,.choices__input:focus{outline:0}.choices__input{display:inline-block;vertical-align:baseline;background-color:#f9f9f9;font-size:14px;margin-bottom:5px;border:0;border-radius:0;max-width:100%;padding:4px 0 4px 2px}.choices__input::-webkit-search-cancel-button,.choices__input::-webkit-search-decoration,.choices__input::-webkit-search-results-button,.choices__input::-webkit-search-results-decoration{display:none}.choices__input::-ms-clear,.choices__input::-ms-reveal{display:none;width:0;height:0}[dir=rtl] .choices__input{padding-right:2px;padding-left:0}.choices__placeholder{opacity:.5}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/e2e/select-multiple.spec.d.ts b/my_flask_app/static/choices.js/public/types/cypress/e2e/select-multiple.spec.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6c3e952d4813acf513cf0851e2cde30911a9b066
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/e2e/select-multiple.spec.d.ts
@@ -0,0 +1 @@
+//# sourceMappingURL=select-multiple.spec.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/e2e/select-multiple.spec.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/e2e/select-multiple.spec.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..fd77f545aa077d645c0d3f68f17be1600cb8cb4f
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/e2e/select-multiple.spec.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"select-multiple.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/e2e/select-multiple.spec.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/e2e/select-one.spec.d.ts b/my_flask_app/static/choices.js/public/types/cypress/e2e/select-one.spec.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9de7a41d062a24c3cb92f3e0b54910f18395cada
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/e2e/select-one.spec.d.ts
@@ -0,0 +1 @@
+//# sourceMappingURL=select-one.spec.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/e2e/select-one.spec.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/e2e/select-one.spec.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..cb7b6e2022ba6d8f5942a9529f743241e1299787
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/e2e/select-one.spec.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"select-one.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/e2e/select-one.spec.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/e2e/text.spec.d.ts b/my_flask_app/static/choices.js/public/types/cypress/e2e/text.spec.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cfb9d2df9b1e56b1d7688c309ff9914230c0844d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/e2e/text.spec.d.ts
@@ -0,0 +1 @@
+//# sourceMappingURL=text.spec.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/e2e/text.spec.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/e2e/text.spec.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..94ccbad20d9f67ef43b5076fa12c83786db7a070
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/e2e/text.spec.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"text.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/e2e/text.spec.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/integration/select-multiple.spec.d.ts b/my_flask_app/static/choices.js/public/types/cypress/integration/select-multiple.spec.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6c3e952d4813acf513cf0851e2cde30911a9b066
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/integration/select-multiple.spec.d.ts
@@ -0,0 +1 @@
+//# sourceMappingURL=select-multiple.spec.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/integration/select-multiple.spec.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/integration/select-multiple.spec.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..b1fdcc1f5662b185fa05b740f2fb975d24f8d016
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/integration/select-multiple.spec.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"select-multiple.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/integration/select-multiple.spec.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/integration/select-one.spec.d.ts b/my_flask_app/static/choices.js/public/types/cypress/integration/select-one.spec.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9de7a41d062a24c3cb92f3e0b54910f18395cada
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/integration/select-one.spec.d.ts
@@ -0,0 +1 @@
+//# sourceMappingURL=select-one.spec.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/integration/select-one.spec.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/integration/select-one.spec.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..d2a6b77bd2605e0b66ecab9ecfeee21531937545
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/integration/select-one.spec.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"select-one.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/integration/select-one.spec.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/integration/text.spec.d.ts b/my_flask_app/static/choices.js/public/types/cypress/integration/text.spec.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cfb9d2df9b1e56b1d7688c309ff9914230c0844d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/integration/text.spec.d.ts
@@ -0,0 +1 @@
+//# sourceMappingURL=text.spec.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/integration/text.spec.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/integration/text.spec.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..cb8c740b29e777b1a24ee751b54cd1ec19c28178
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/integration/text.spec.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"text.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/integration/text.spec.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/plugins/index.d.ts b/my_flask_app/static/choices.js/public/types/cypress/plugins/index.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2e1bed6c46eeedfcebff730eb05bc24c92f23a33
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/plugins/index.d.ts
@@ -0,0 +1,3 @@
+declare function _exports(on: any, config: any): void;
+export = _exports;
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/plugins/index.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/plugins/index.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..51c72de17d77aab5b51f30cdf7009d2a9c52195b
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/plugins/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../cypress/plugins/index.js"],"names":[],"mappings":"AAaiB,sDAGhB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/support/commands.d.ts b/my_flask_app/static/choices.js/public/types/cypress/support/commands.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b7ad2b326237616a326428540bb3cd247cf59718
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/support/commands.d.ts
@@ -0,0 +1 @@
+//# sourceMappingURL=commands.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/support/commands.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/support/commands.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..b4cb532487f695305112f5c417575def15108d25
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/support/commands.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../cypress/support/commands.js"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/support/e2e.d.ts b/my_flask_app/static/choices.js/public/types/cypress/support/e2e.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aafedb01df2de93dc68a64dcf1e8221c7f63a6a2
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/support/e2e.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=e2e.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/support/e2e.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/support/e2e.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..9d3830302809648026fa3a002441aea714c4beef
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/support/e2e.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"e2e.d.ts","sourceRoot":"","sources":["../../../../cypress/support/e2e.js"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/support/index.d.ts b/my_flask_app/static/choices.js/public/types/cypress/support/index.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e26a57a8ca84c682b2b77b57b9d6e340ffd33436
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/support/index.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/cypress/support/index.d.ts.map b/my_flask_app/static/choices.js/public/types/cypress/support/index.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..02df402868814037c26dd2ee98f0c1821e172b7e
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/cypress/support/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../cypress/support/index.js"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/index.d.ts b/my_flask_app/static/choices.js/public/types/src/index.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dee565e399c07100349bd24305840e4517f49a54
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/index.d.ts
@@ -0,0 +1,7 @@
+import Choices from './scripts/choices';
+export * from './scripts/interfaces';
+export * from './scripts/constants';
+export * from './scripts/defaults';
+export { default as templates } from './scripts/templates';
+export default Choices;
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/index.d.ts.map b/my_flask_app/static/choices.js/public/types/src/index.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..c7f1a92370e8abc9c53ff2ce88818e996b0e1559
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,mBAAmB,CAAC;AAExC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE3D,eAAe,OAAO,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61a1b30e5ff2df1fdf63211a9bbb4d80c14ee98a
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.d.ts
@@ -0,0 +1,44 @@
+import { ACTION_TYPES } from '../constants';
+import { Choice } from '../interfaces/choice';
+export interface AddChoiceAction {
+    type: typeof ACTION_TYPES.ADD_CHOICE;
+    id: number;
+    value: string;
+    label: string;
+    groupId: number;
+    disabled: boolean;
+    elementId: number;
+    customProperties: object;
+    placeholder: boolean;
+    keyCode: number;
+}
+export interface Result<T> {
+    item: T;
+    score: number;
+}
+export interface FilterChoicesAction {
+    type: typeof ACTION_TYPES.FILTER_CHOICES;
+    results: Result<Choice>[];
+}
+export interface ActivateChoicesAction {
+    type: typeof ACTION_TYPES.ACTIVATE_CHOICES;
+    active: boolean;
+}
+export interface ClearChoicesAction {
+    type: typeof ACTION_TYPES.CLEAR_CHOICES;
+}
+export declare const addChoice: ({ value, label, id, groupId, disabled, elementId, customProperties, placeholder, keyCode, }: {
+    value: any;
+    label: any;
+    id: any;
+    groupId: any;
+    disabled: any;
+    elementId: any;
+    customProperties: any;
+    placeholder: any;
+    keyCode: any;
+}) => AddChoiceAction;
+export declare const filterChoices: (results: Result<Choice>[]) => FilterChoicesAction;
+export declare const activateChoices: (active?: boolean) => ActivateChoicesAction;
+export declare const clearChoices: () => ClearChoicesAction;
+//# sourceMappingURL=choices.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..2e69e60c7b72f9259217c5343978021d3f7fb335
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"choices.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/choices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,YAAY,CAAC,UAAU,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,YAAY,CAAC,cAAc,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,OAAO,YAAY,CAAC,gBAAgB,CAAC;IAC3C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,YAAY,CAAC,aAAa,CAAC;CACzC;AAED,eAAO,MAAM,SAAS;;;;;;;;;;MAUlB,eAWF,CAAC;AAEH,eAAO,MAAM,aAAa,YACf,OAAO,MAAM,CAAC,EAAE,KACxB,mBAGD,CAAC;AAEH,eAAO,MAAM,eAAe,wBAAoB,qBAG9C,CAAC;AAEH,eAAO,MAAM,YAAY,QAAO,kBAE9B,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1b34de17b8e7deae827e04c0b9d2be8057711734
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=choices.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..0f1c3a57739cb1f6bd7aa26b86a831c1195e11a4
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/choices.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"choices.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/choices.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ad7a8b7c749fa252597d55cb5527d9473d6ef134
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.d.ts
@@ -0,0 +1,15 @@
+import { ACTION_TYPES } from '../constants';
+export interface AddGroupAction {
+    type: typeof ACTION_TYPES.ADD_GROUP;
+    id: number;
+    value: string;
+    active: boolean;
+    disabled: boolean;
+}
+export declare const addGroup: ({ value, id, active, disabled, }: {
+    id: number;
+    value: string;
+    active: boolean;
+    disabled: boolean;
+}) => AddGroupAction;
+//# sourceMappingURL=groups.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..4fd751608a9ce87559686ffa4c42bd18450bc1f7
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/groups.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,YAAY,CAAC,SAAS,CAAC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,QAAQ;QAMf,MAAM;WACH,MAAM;YACL,OAAO;cACL,OAAO;MACf,cAMF,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8d44d56ef8067321d75fd258f67bcd41b36ba371
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=groups.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..80ca388ff8570a69e736ef32a2a35a42523d4c0c
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/groups.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"groups.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/groups.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..96b0b2a8b971066d28a8640b5284e060434f6713
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.d.ts
@@ -0,0 +1,35 @@
+import { ACTION_TYPES } from '../constants';
+export interface AddItemAction {
+    type: typeof ACTION_TYPES.ADD_ITEM;
+    id: number;
+    value: string;
+    label: string;
+    choiceId: number;
+    groupId: number;
+    customProperties: object;
+    placeholder: boolean;
+    keyCode: number;
+}
+export interface RemoveItemAction {
+    type: typeof ACTION_TYPES.REMOVE_ITEM;
+    id: number;
+    choiceId: number;
+}
+export interface HighlightItemAction {
+    type: typeof ACTION_TYPES.HIGHLIGHT_ITEM;
+    id: number;
+    highlighted: boolean;
+}
+export declare const addItem: ({ value, label, id, choiceId, groupId, customProperties, placeholder, keyCode, }: {
+    id: number;
+    value: string;
+    label: string;
+    choiceId: number;
+    groupId: number;
+    customProperties: object;
+    placeholder: boolean;
+    keyCode: number;
+}) => AddItemAction;
+export declare const removeItem: (id: number, choiceId: number) => RemoveItemAction;
+export declare const highlightItem: (id: number, highlighted: boolean) => HighlightItemAction;
+//# sourceMappingURL=items.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..2ad25909931ecbf181497dbf660e97350c75ac50
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"items.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/items.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,YAAY,CAAC,QAAQ,CAAC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,YAAY,CAAC,WAAW,CAAC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,YAAY,CAAC,cAAc,CAAC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,OAAO;QAUd,MAAM;WACH,MAAM;WACN,MAAM;cACH,MAAM;aACP,MAAM;sBACG,MAAM;iBACX,OAAO;aACX,MAAM;MACb,aAUF,CAAC;AAEH,eAAO,MAAM,UAAU,OAAQ,MAAM,YAAY,MAAM,KAAG,gBAIxD,CAAC;AAEH,eAAO,MAAM,aAAa,OACpB,MAAM,eACG,OAAO,KACnB,mBAID,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e81d58b44415bfe62423891267a9482477396723
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=items.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..77773c21a8c4b627706cd721e37820c27c8f4e87
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/items.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"items.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/items.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..65e1faf1c0dd8660ff3f4677d349a024815d241d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.d.ts
@@ -0,0 +1,17 @@
+import { ACTION_TYPES } from '../constants';
+import { State } from '../interfaces/state';
+export interface ClearAllAction {
+    type: typeof ACTION_TYPES.CLEAR_ALL;
+}
+export interface ResetToAction {
+    type: typeof ACTION_TYPES.RESET_TO;
+    state: State;
+}
+export interface SetIsLoadingAction {
+    type: typeof ACTION_TYPES.SET_IS_LOADING;
+    isLoading: boolean;
+}
+export declare const clearAll: () => ClearAllAction;
+export declare const resetTo: (state: State) => ResetToAction;
+export declare const setIsLoading: (isLoading: boolean) => SetIsLoadingAction;
+//# sourceMappingURL=misc.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..0e77eb24b491ccef9d6e9d7fc1c3700b314e70f5
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/misc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,YAAY,CAAC,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,YAAY,CAAC,QAAQ,CAAC;IACnC,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,YAAY,CAAC,cAAc,CAAC;IACzC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,QAAQ,QAAO,cAE1B,CAAC;AAEH,eAAO,MAAM,OAAO,UAAW,KAAK,KAAG,aAGrC,CAAC;AAEH,eAAO,MAAM,YAAY,cAAe,OAAO,KAAG,kBAGhD,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c81b2ccc6ac25e8c3ef790b337e0c4c30c4a728f
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=misc.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..912fa1897225fd7b9ea773bc8c007f6baf46f4d1
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/actions/misc.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"misc.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/misc.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/choices.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/choices.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..accf731a8574b30b976d1ed4c33711cc3adfcc44
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/choices.d.ts
@@ -0,0 +1,218 @@
+import { Container, Dropdown, Input, List, WrappedInput, WrappedSelect } from './components';
+import { Choice } from './interfaces/choice';
+import { Group } from './interfaces/group';
+import { Item } from './interfaces/item';
+import { Notice } from './interfaces/notice';
+import { Options } from './interfaces/options';
+import { State } from './interfaces/state';
+import Store from './store/store';
+import templates from './templates';
+/**
+ * Choices
+ * @author Josh Johnson<josh@joshuajohnson.co.uk>
+ */
+declare class Choices implements Choices {
+    static get defaults(): {
+        options: Partial<Options>;
+        templates: typeof templates;
+    };
+    initialised: boolean;
+    config: Options;
+    passedElement: WrappedInput | WrappedSelect;
+    containerOuter: Container;
+    containerInner: Container;
+    choiceList: List;
+    itemList: List;
+    input: Input;
+    dropdown: Dropdown;
+    _isTextElement: boolean;
+    _isSelectOneElement: boolean;
+    _isSelectMultipleElement: boolean;
+    _isSelectElement: boolean;
+    _store: Store;
+    _templates: typeof templates;
+    _initialState: State;
+    _currentState: State;
+    _prevState: State;
+    _currentValue: string;
+    _canSearch: boolean;
+    _isScrollingOnIe: boolean;
+    _highlightPosition: number;
+    _wasTap: boolean;
+    _isSearching: boolean;
+    _placeholderValue: string | null;
+    _baseId: string;
+    _direction: HTMLElement['dir'];
+    _idNames: {
+        itemChoice: string;
+    };
+    _presetGroups: Group[] | HTMLOptGroupElement[] | Element[];
+    _presetOptions: Item[] | HTMLOptionElement[];
+    _presetChoices: Partial<Choice>[];
+    _presetItems: Item[] | string[];
+    constructor(element?: string | Element | HTMLInputElement | HTMLSelectElement, userConfig?: Partial<Options>);
+    init(): void;
+    destroy(): void;
+    enable(): this;
+    disable(): this;
+    highlightItem(item: Item, runEvent?: boolean): this;
+    unhighlightItem(item: Item): this;
+    highlightAll(): this;
+    unhighlightAll(): this;
+    removeActiveItemsByValue(value: string): this;
+    removeActiveItems(excludedId: number): this;
+    removeHighlightedItems(runEvent?: boolean): this;
+    showDropdown(preventInputFocus?: boolean): this;
+    hideDropdown(preventInputBlur?: boolean): this;
+    getValue(valueOnly?: boolean): string[] | Item[] | Item | string;
+    setValue(items: string[] | Item[]): this;
+    setChoiceByValue(value: string | string[]): this;
+    /**
+     * Set choices of select input via an array of objects (or function that returns array of object or promise of it),
+     * a value field name and a label field name.
+     * This behaves the same as passing items via the choices option but can be called after initialising Choices.
+     * This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices.
+     * Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc).
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @example
+     * ```js
+     * const example = new Choices(element);
+     *
+     * example.setChoices([
+     *   {value: 'One', label: 'Label One', disabled: true},
+     *   {value: 'Two', label: 'Label Two', selected: true},
+     *   {value: 'Three', label: 'Label Three'},
+     * ], 'value', 'label', false);
+     * ```
+     *
+     * @example
+     * ```js
+     * const example = new Choices(element);
+     *
+     * example.setChoices(async () => {
+     *   try {
+     *      const items = await fetch('/items');
+     *      return items.json()
+     *   } catch(err) {
+     *      console.error(err)
+     *   }
+     * });
+     * ```
+     *
+     * @example
+     * ```js
+     * const example = new Choices(element);
+     *
+     * example.setChoices([{
+     *   label: 'Group one',
+     *   id: 1,
+     *   disabled: false,
+     *   choices: [
+     *     {value: 'Child One', label: 'Child One', selected: true},
+     *     {value: 'Child Two', label: 'Child Two',  disabled: true},
+     *     {value: 'Child Three', label: 'Child Three'},
+     *   ]
+     * },
+     * {
+     *   label: 'Group two',
+     *   id: 2,
+     *   disabled: false,
+     *   choices: [
+     *     {value: 'Child Four', label: 'Child Four', disabled: true},
+     *     {value: 'Child Five', label: 'Child Five'},
+     *     {value: 'Child Six', label: 'Child Six', customProperties: {
+     *       description: 'Custom description about child six',
+     *       random: 'Another random custom property'
+     *     }},
+     *   ]
+     * }], 'value', 'label', false);
+     * ```
+     */
+    setChoices(choicesArrayOrFetcher?: Choice[] | Group[] | ((instance: Choices) => Choice[] | Promise<Choice[]>), value?: string, label?: string, replaceChoices?: boolean): this | Promise<this>;
+    clearChoices(): this;
+    clearStore(): this;
+    clearInput(): this;
+    _render(): void;
+    _renderChoices(): void;
+    _renderItems(): void;
+    _createGroupsFragment(groups: Group[], choices: Choice[], fragment?: DocumentFragment): DocumentFragment;
+    _createChoicesFragment(choices: Choice[], fragment?: DocumentFragment, withinGroup?: boolean): DocumentFragment;
+    _createItemsFragment(items: Item[], fragment?: DocumentFragment): DocumentFragment;
+    _triggerChange(value: any): void;
+    _selectPlaceholderChoice(placeholderChoice: Choice): void;
+    _handleButtonAction(activeItems?: Item[], element?: HTMLElement): void;
+    _handleItemAction(activeItems?: Item[], element?: HTMLElement, hasShiftKey?: boolean): void;
+    _handleChoiceAction(activeItems?: Item[], element?: HTMLElement): void;
+    _handleBackspace(activeItems?: Item[]): void;
+    _startLoading(): void;
+    _stopLoading(): void;
+    _handleLoadingState(setLoading?: boolean): void;
+    _handleSearch(value: string): void;
+    _canAddItem(activeItems: Item[], value: string): Notice;
+    _searchChoices(value: string): number;
+    _addEventListeners(): void;
+    _removeEventListeners(): void;
+    _onKeyDown(event: KeyboardEvent): void;
+    _onKeyUp({ target, keyCode, }: Pick<KeyboardEvent, 'target' | 'keyCode'>): void;
+    _onSelectKey(event: KeyboardEvent, hasItems: boolean): void;
+    _onEnterKey(event: KeyboardEvent, activeItems: Item[], hasActiveDropdown: boolean): void;
+    _onEscapeKey(hasActiveDropdown: boolean): void;
+    _onDirectionKey(event: KeyboardEvent, hasActiveDropdown: boolean): void;
+    _onDeleteKey(event: KeyboardEvent, activeItems: Item[], hasFocusedInput: boolean): void;
+    _onTouchMove(): void;
+    _onTouchEnd(event: TouchEvent): void;
+    /**
+     * Handles mousedown event in capture mode for containetOuter.element
+     */
+    _onMouseDown(event: MouseEvent): void;
+    /**
+     * Handles mouseover event over this.dropdown
+     * @param {MouseEvent} event
+     */
+    _onMouseOver({ target }: Pick<MouseEvent, 'target'>): void;
+    _onClick({ target }: Pick<MouseEvent, 'target'>): void;
+    _onFocus({ target }: Pick<FocusEvent, 'target'>): void;
+    _onBlur({ target }: Pick<FocusEvent, 'target'>): void;
+    _onFormReset(): void;
+    _highlightChoice(el?: HTMLElement | null): void;
+    _addItem({ value, label, choiceId, groupId, customProperties, placeholder, keyCode, }: {
+        value: string;
+        label?: string | null;
+        choiceId?: number;
+        groupId?: number;
+        customProperties?: object;
+        placeholder?: boolean;
+        keyCode?: number;
+    }): void;
+    _removeItem(item: Item): void;
+    _addChoice({ value, label, isSelected, isDisabled, groupId, customProperties, placeholder, keyCode, }: {
+        value: string;
+        label?: string | null;
+        isSelected?: boolean;
+        isDisabled?: boolean;
+        groupId?: number;
+        customProperties?: Record<string, any>;
+        placeholder?: boolean;
+        keyCode?: number;
+    }): void;
+    _addGroup({ group, id, valueKey, labelKey }: {
+        group: any;
+        id: any;
+        valueKey?: string | undefined;
+        labelKey?: string | undefined;
+    }): void;
+    _getTemplate(template: string, ...args: any): any;
+    _createTemplates(): void;
+    _createElements(): void;
+    _createStructure(): void;
+    _addPredefinedGroups(groups: Group[] | HTMLOptGroupElement[] | Element[]): void;
+    _addPredefinedChoices(choices: Partial<Choice>[]): void;
+    _addPredefinedItems(items: Item[] | string[]): void;
+    _setChoiceOrItem(item: any): void;
+    _findAndSelectChoiceByValue(value: string): void;
+    _generatePlaceholderValue(): string | null;
+}
+export default Choices;
+//# sourceMappingURL=choices.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/choices.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/choices.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..32cb7a2e548d6608ae09971111f39c7b91c04bbc
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/choices.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"choices.d.ts","sourceRoot":"","sources":["../../../../src/scripts/choices.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,SAAS,EACT,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,aAAa,EACd,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAe3C,OAAO,KAAK,MAAM,eAAe,CAAC;AAClC,OAAO,SAAS,MAAM,aAAa,CAAC;AASpC;;;GAGG;AACH,cAAM,OAAQ,YAAW,OAAO;IAC9B,MAAM,KAAK,QAAQ,IAAI;QACrB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,SAAS,EAAE,OAAO,SAAS,CAAC;KAC7B,CASA;IAED,WAAW,EAAE,OAAO,CAAC;IAErB,MAAM,EAAE,OAAO,CAAC;IAEhB,aAAa,EAAE,YAAY,GAAG,aAAa,CAAC;IAE5C,cAAc,EAAE,SAAS,CAAC;IAE1B,cAAc,EAAE,SAAS,CAAC;IAE1B,UAAU,EAAE,IAAI,CAAC;IAEjB,QAAQ,EAAE,IAAI,CAAC;IAEf,KAAK,EAAE,KAAK,CAAC;IAEb,QAAQ,EAAE,QAAQ,CAAC;IAEnB,cAAc,EAAE,OAAO,CAAC;IAExB,mBAAmB,EAAE,OAAO,CAAC;IAE7B,wBAAwB,EAAE,OAAO,CAAC;IAElC,gBAAgB,EAAE,OAAO,CAAC;IAE1B,MAAM,EAAE,KAAK,CAAC;IAEd,UAAU,EAAE,OAAO,SAAS,CAAC;IAE7B,aAAa,EAAE,KAAK,CAAC;IAErB,aAAa,EAAE,KAAK,CAAC;IAErB,UAAU,EAAE,KAAK,CAAC;IAElB,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;IAEpB,gBAAgB,EAAE,OAAO,CAAC;IAE1B,kBAAkB,EAAE,MAAM,CAAC;IAE3B,OAAO,EAAE,OAAO,CAAC;IAEjB,YAAY,EAAE,OAAO,CAAC;IAEtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC,OAAO,EAAE,MAAM,CAAC;IAEhB,UAAU,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IAE/B,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,aAAa,EAAE,KAAK,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,EAAE,CAAC;IAE3D,cAAc,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAE7C,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IAElC,YAAY,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;gBAG9B,OAAO,GACH,MAAM,GACN,OAAO,GACP,gBAAgB,GAChB,iBAAmC,EACvC,UAAU,GAAE,OAAO,CAAC,OAAO,CAAM;IAuLnC,IAAI,IAAI,IAAI;IA+BZ,OAAO,IAAI,IAAI;IAmBf,MAAM,IAAI,IAAI;IAcd,OAAO,IAAI,IAAI;IAcf,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,UAAO,GAAG,IAAI;IAsBhD,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAmBjC,YAAY,IAAI,IAAI;IAMpB,cAAc,IAAI,IAAI;IAMtB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ7C,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAQ3C,sBAAsB,CAAC,QAAQ,UAAQ,GAAG,IAAI;IAa9C,YAAY,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,IAAI;IAmB/C,YAAY,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,IAAI;IAoB9C,QAAQ,CAAC,SAAS,UAAQ,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,GAAG,MAAM;IAc9D,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI;IAUxC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAchD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8DG;IACH,UAAU,CACR,qBAAqB,GACjB,MAAM,EAAE,GACR,KAAK,EAAE,GACP,CAAC,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAM,EAC9D,KAAK,SAAU,EACf,KAAK,SAAU,EACf,cAAc,UAAQ,GACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA+FvB,YAAY,IAAI,IAAI;IAMpB,UAAU,IAAI,IAAI;IAMlB,UAAU,IAAI,IAAI;IAYlB,OAAO,IAAI,IAAI;IA8Bf,cAAc,IAAI,IAAI;IA6EtB,YAAY,IAAI,IAAI;IAcpB,qBAAqB,CACnB,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,GAAE,gBAAoD,GAC7D,gBAAgB;IA8BnB,sBAAsB,CACpB,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,GAAE,gBAAoD,EAC9D,WAAW,UAAQ,GAClB,gBAAgB;IAyEnB,oBAAoB,CAClB,KAAK,EAAE,IAAI,EAAE,EACb,QAAQ,GAAE,gBAAoD,GAC7D,gBAAgB;IAgCnB,cAAc,CAAC,KAAK,KAAA,GAAG,IAAI;IAU3B,wBAAwB,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI;IAYzD,mBAAmB,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IA4BtE,iBAAiB,CACf,WAAW,CAAC,EAAE,IAAI,EAAE,EACpB,OAAO,CAAC,EAAE,WAAW,EACrB,WAAW,UAAQ,GAClB,IAAI;IA4BP,mBAAmB,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IAoDtE,gBAAgB,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI;IAwB5C,aAAa,IAAI,IAAI;IAIrB,YAAY,IAAI,IAAI;IAIpB,mBAAmB,CAAC,UAAU,UAAO,GAAG,IAAI;IAuC5C,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IA4BlC,WAAW,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAwDvD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IA6BrC,kBAAkB,IAAI,IAAI;IAsD1B,qBAAqB,IAAI,IAAI;IAmC7B,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAsDtC,QAAQ,CAAC,EACP,MAAM,EACN,OAAO,GACR,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC,GAAG,IAAI;IAoCnD,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAmB3D,WAAW,CACT,KAAK,EAAE,aAAa,EACpB,WAAW,EAAE,IAAI,EAAE,EACnB,iBAAiB,EAAE,OAAO,GACzB,IAAI;IA2CP,YAAY,CAAC,iBAAiB,EAAE,OAAO,GAAG,IAAI;IAO9C,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,OAAO,GAAG,IAAI;IAgEvE,YAAY,CACV,KAAK,EAAE,aAAa,EACpB,WAAW,EAAE,IAAI,EAAE,EACnB,eAAe,EAAE,OAAO,GACvB,IAAI;IAaP,YAAY,IAAI,IAAI;IAMpB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAyBpC;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAyCrC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAI;IAM1D,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAI;IAkCtD,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAI;IAiCtD,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAI;IA+CrD,YAAY,IAAI,IAAI;IAIpB,gBAAgB,CAAC,EAAE,GAAE,WAAW,GAAG,IAAW,GAAG,IAAI;IAmDrD,QAAQ,CAAC,EACP,KAAK,EACL,KAAY,EACZ,QAAa,EACb,OAAY,EACZ,gBAAqB,EACrB,WAAmB,EACnB,OAAY,GACb,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IA+CR,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAmB7B,UAAU,CAAC,EACT,KAAK,EACL,KAAY,EACZ,UAAkB,EAClB,UAAkB,EAClB,OAAY,EACZ,gBAAqB,EACrB,WAAmB,EACnB,OAAY,GACb,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACvC,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IAqCR,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,QAAkB,EAAE,QAAkB,EAAE;;;;;KAAA,GAAG,IAAI;IA6CtE,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,GAAG,GAAG;IAIjD,gBAAgB,IAAI,IAAI;IAcxB,eAAe,IAAI,IAAI;IA6CvB,gBAAgB,IAAI,IAAI;IAmDxB,oBAAoB,CAClB,MAAM,EAAE,KAAK,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,EAAE,GAClD,IAAI;IA2BP,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI;IA0DvD,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI;IAoBnD,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAgDjC,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAoBhD,yBAAyB,IAAI,MAAM,GAAG,IAAI;CA2B3C;AAED,eAAe,OAAO,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/choices.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/choices.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1b34de17b8e7deae827e04c0b9d2be8057711734
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/choices.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=choices.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/choices.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/choices.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..13f8b43e0ee7795916e42a548a03ac75389d1466
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/choices.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"choices.test.d.ts","sourceRoot":"","sources":["../../../../src/scripts/choices.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/container.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/container.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ad8ac4e6ade18389cfbd28ac951ecfb6bac39f78
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/container.d.ts
@@ -0,0 +1,43 @@
+import { ClassNames } from '../interfaces/class-names';
+import { PositionOptionsType } from '../interfaces/position-options-type';
+import { PassedElementType } from '../interfaces/passed-element-type';
+export default class Container {
+    element: HTMLElement;
+    type: PassedElementType;
+    classNames: ClassNames;
+    position: PositionOptionsType;
+    isOpen: boolean;
+    isFlipped: boolean;
+    isFocussed: boolean;
+    isDisabled: boolean;
+    isLoading: boolean;
+    constructor({ element, type, classNames, position, }: {
+        element: HTMLElement;
+        type: PassedElementType;
+        classNames: ClassNames;
+        position: PositionOptionsType;
+    });
+    addEventListeners(): void;
+    removeEventListeners(): void;
+    /**
+     * Determine whether container should be flipped based on passed
+     * dropdown position
+     */
+    shouldFlip(dropdownPos: number): boolean;
+    setActiveDescendant(activeDescendantID: string): void;
+    removeActiveDescendant(): void;
+    open(dropdownPos: number): void;
+    close(): void;
+    focus(): void;
+    addFocusState(): void;
+    removeFocusState(): void;
+    enable(): void;
+    disable(): void;
+    wrap(element: HTMLSelectElement | HTMLInputElement | HTMLElement): void;
+    unwrap(element: HTMLElement): void;
+    addLoadingState(): void;
+    removeLoadingState(): void;
+    _onFocus(): void;
+    _onBlur(): void;
+}
+//# sourceMappingURL=container.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/container.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/container.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..e253b2fb08e738e1d9909785fadf95de30a38fda
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/container.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/container.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B,OAAO,EAAE,WAAW,CAAC;IAErB,IAAI,EAAE,iBAAiB,CAAC;IAExB,UAAU,EAAE,UAAU,CAAC;IAEvB,QAAQ,EAAE,mBAAmB,CAAC;IAE9B,MAAM,EAAE,OAAO,CAAC;IAEhB,SAAS,EAAE,OAAO,CAAC;IAEnB,UAAU,EAAE,OAAO,CAAC;IAEpB,UAAU,EAAE,OAAO,CAAC;IAEpB,SAAS,EAAE,OAAO,CAAC;gBAEP,EACV,OAAO,EACP,IAAI,EACJ,UAAU,EACV,QAAQ,GACT,EAAE;QACD,OAAO,EAAE,WAAW,CAAC;QACrB,IAAI,EAAE,iBAAiB,CAAC;QACxB,UAAU,EAAE,UAAU,CAAC;QACvB,QAAQ,EAAE,mBAAmB,CAAC;KAC/B;IAcD,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAK5B;;;OAGG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAkBxC,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI;IAIrD,sBAAsB,IAAI,IAAI;IAI9B,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAW/B,KAAK,IAAI,IAAI;IAab,KAAK,IAAI,IAAI;IAMb,aAAa,IAAI,IAAI;IAIrB,gBAAgB,IAAI,IAAI;IAIxB,MAAM,IAAI,IAAI;IASd,OAAO,IAAI,IAAI;IASf,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,WAAW,GAAG,IAAI;IAIvE,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IASlC,eAAe,IAAI,IAAI;IAMvB,kBAAkB,IAAI,IAAI;IAM1B,QAAQ,IAAI,IAAI;IAIhB,OAAO,IAAI,IAAI;CAGhB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/container.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/container.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1628d9d7d248950a1aa8216a5dfd6d3b082def35
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/container.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=container.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/container.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/container.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..85b9faa6b44d775d28648c733427abe6b477b0c4
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/container.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"container.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/container.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6f574251eeb0d2da2f3c84f456da8b70abea99a
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.d.ts
@@ -0,0 +1,27 @@
+import { ClassNames } from '../interfaces/class-names';
+import { PassedElementType } from '../interfaces/passed-element-type';
+export default class Dropdown {
+    element: HTMLElement;
+    type: PassedElementType;
+    classNames: ClassNames;
+    isActive: boolean;
+    constructor({ element, type, classNames, }: {
+        element: HTMLElement;
+        type: PassedElementType;
+        classNames: ClassNames;
+    });
+    /**
+     * Bottom position of dropdown in viewport coordinates
+     */
+    get distanceFromTopWindow(): number;
+    getChild(selector: string): HTMLElement | null;
+    /**
+     * Show dropdown to user by adding active state class
+     */
+    show(): this;
+    /**
+     * Hide dropdown from user
+     */
+    hide(): this;
+}
+//# sourceMappingURL=dropdown.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..129ec116cda2937450305bd59e47c9be6eb14418
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"dropdown.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/dropdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,CAAC,OAAO,OAAO,QAAQ;IAC3B,OAAO,EAAE,WAAW,CAAC;IAErB,IAAI,EAAE,iBAAiB,CAAC;IAExB,UAAU,EAAE,UAAU,CAAC;IAEvB,QAAQ,EAAE,OAAO,CAAC;gBAEN,EACV,OAAO,EACP,IAAI,EACJ,UAAU,GACX,EAAE;QACD,OAAO,EAAE,WAAW,CAAC;QACrB,IAAI,EAAE,iBAAiB,CAAC;QACxB,UAAU,EAAE,UAAU,CAAC;KACxB;IAOD;;OAEG;IACH,IAAI,qBAAqB,IAAI,MAAM,CAElC;IAED,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAI9C;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;OAEG;IACH,IAAI,IAAI,IAAI;CAOb"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2dff0fae52a9f3bbdfa0031564c96465cca6a3dd
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=dropdown.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..4c26ccd8c11eee5d588753d63146821b98a93f7e
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/dropdown.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"dropdown.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/dropdown.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/index.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/index.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b37addf936216a19c194b2a92e086248265b83a9
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/index.d.ts
@@ -0,0 +1,8 @@
+import Dropdown from './dropdown';
+import Container from './container';
+import Input from './input';
+import List from './list';
+import WrappedInput from './wrapped-input';
+import WrappedSelect from './wrapped-select';
+export { Dropdown, Container, Input, List, WrappedInput, WrappedSelect };
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/index.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/index.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..4e478ab46f251c042eabf49b801ebc594630d04d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,SAAS,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,IAAI,MAAM,QAAQ,CAAC;AAC1B,OAAO,YAAY,MAAM,iBAAiB,CAAC;AAC3C,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/input.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/input.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3b36571ddb4d63f51bbd0913bd7fc280ed237e2c
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/input.d.ts
@@ -0,0 +1,39 @@
+import { ClassNames } from '../interfaces/class-names';
+import { PassedElementType } from '../interfaces/passed-element-type';
+export default class Input {
+    element: HTMLInputElement;
+    type: PassedElementType;
+    classNames: ClassNames;
+    preventPaste: boolean;
+    isFocussed: boolean;
+    isDisabled: boolean;
+    constructor({ element, type, classNames, preventPaste, }: {
+        element: HTMLInputElement;
+        type: PassedElementType;
+        classNames: ClassNames;
+        preventPaste: boolean;
+    });
+    set placeholder(placeholder: string);
+    get value(): string;
+    set value(value: string);
+    get rawValue(): string;
+    addEventListeners(): void;
+    removeEventListeners(): void;
+    enable(): void;
+    disable(): void;
+    focus(): void;
+    blur(): void;
+    clear(setWidth?: boolean): this;
+    /**
+     * Set the correct input width based on placeholder
+     * value or input value
+     */
+    setWidth(): void;
+    setActiveDescendant(activeDescendantID: string): void;
+    removeActiveDescendant(): void;
+    _onInput(): void;
+    _onPaste(event: ClipboardEvent): void;
+    _onFocus(): void;
+    _onBlur(): void;
+}
+//# sourceMappingURL=input.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/input.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/input.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..8828eb16f115560bf4db667f9087284def8fa4f3
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/input.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/input.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,OAAO,EAAE,gBAAgB,CAAC;IAE1B,IAAI,EAAE,iBAAiB,CAAC;IAExB,UAAU,EAAE,UAAU,CAAC;IAEvB,YAAY,EAAE,OAAO,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;IAEpB,UAAU,EAAE,OAAO,CAAC;gBAER,EACV,OAAO,EACP,IAAI,EACJ,UAAU,EACV,YAAY,GACb,EAAE;QACD,OAAO,EAAE,gBAAgB,CAAC;QAC1B,IAAI,EAAE,iBAAiB,CAAC;QACxB,UAAU,EAAE,UAAU,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;KACvB;IAcD,IAAI,WAAW,CAAC,WAAW,EAAE,MAAM,EAElC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAEtB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,iBAAiB,IAAI,IAAI;IAazB,oBAAoB,IAAI,IAAI;IAO5B,MAAM,IAAI,IAAI;IAKd,OAAO,IAAI,IAAI;IAKf,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAMZ,KAAK,CAAC,QAAQ,UAAO,GAAG,IAAI;IAY5B;;;OAGG;IACH,QAAQ,IAAI,IAAI;IAOhB,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI;IAIrD,sBAAsB,IAAI,IAAI;IAI9B,QAAQ,IAAI,IAAI;IAMhB,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAMrC,QAAQ,IAAI,IAAI;IAIhB,OAAO,IAAI,IAAI;CAGhB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/input.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/input.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f6118e429a805c1461d20e5b8e3a6cf234d70f15
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/input.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=input.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/input.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/input.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..be433685320532a682c4bd00785d5473140577d0
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/input.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"input.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/input.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/list.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/list.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..51a086854a6ad5f48a3bdb73934417731893516d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/list.d.ts
@@ -0,0 +1,18 @@
+export default class List {
+    element: HTMLElement;
+    scrollPos: number;
+    height: number;
+    constructor({ element }: {
+        element: HTMLElement;
+    });
+    clear(): void;
+    append(node: Element | DocumentFragment): void;
+    getChild(selector: string): HTMLElement | null;
+    hasChildren(): boolean;
+    scrollToTop(): void;
+    scrollToChildElement(element: HTMLElement, direction: 1 | -1): void;
+    _scrollDown(scrollPos: number, strength: number, destination: number): void;
+    _scrollUp(scrollPos: number, strength: number, destination: number): void;
+    _animateScroll(destination: number, direction: number): void;
+}
+//# sourceMappingURL=list.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/list.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/list.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..8b9068f429e116803b5616b0b86503c8ab479605
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/list.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/list.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,IAAI;IACvB,OAAO,EAAE,WAAW,CAAC;IAErB,SAAS,EAAE,MAAM,CAAC;IAElB,MAAM,EAAE,MAAM,CAAC;gBAEH,EAAE,OAAO,EAAE,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE;IAMjD,KAAK,IAAI,IAAI;IAIb,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI;IAI9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAI9C,WAAW,IAAI,OAAO;IAItB,WAAW,IAAI,IAAI;IAInB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI;IAwBnE,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAO3E,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAOzE,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAyB7D"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/list.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/list.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ce43e8d4083c07cf614dfb0e031edb3707a8118e
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/list.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=list.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/list.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/list.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..ad131680bdbd1163415dea0cc142d4fd85a3265a
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/list.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"list.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/list.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ae18b82e5b5cf58cae6a786f478ef8732c58d2f8
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.d.ts
@@ -0,0 +1,21 @@
+import { ClassNames } from '../interfaces/class-names';
+import { EventType } from '../interfaces/event-type';
+export default class WrappedElement {
+    element: HTMLInputElement | HTMLSelectElement;
+    classNames: ClassNames;
+    isDisabled: boolean;
+    constructor({ element, classNames }: {
+        element: any;
+        classNames: any;
+    });
+    get isActive(): boolean;
+    get dir(): string;
+    get value(): string;
+    set value(value: string);
+    conceal(): void;
+    reveal(): void;
+    enable(): void;
+    disable(): void;
+    triggerEvent(eventType: EventType, data?: object): void;
+}
+//# sourceMappingURL=wrapped-element.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..2df95bf8ee3a5d2e2bc6702ad78f5b5de61cfa01
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wrapped-element.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAGrD,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IAE9C,UAAU,EAAE,UAAU,CAAC;IAEvB,UAAU,EAAE,OAAO,CAAC;gBAER,EAAE,OAAO,EAAE,UAAU,EAAE;;;KAAA;IAcnC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,OAAO,IAAI,IAAI;IAkBf,MAAM,IAAI,IAAI;IAsBd,MAAM,IAAI,IAAI;IAMd,OAAO,IAAI,IAAI;IAMf,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;CAGxD"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a9838b85178701be147f3c296aff58cc7ebe6635
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=wrapped-element.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..fb26915d64138d19df225d4dbe88d0afac7f6b84
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-element.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wrapped-element.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-element.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4c35240258a6d35c0aea60a93749db8e9f2f192
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.d.ts
@@ -0,0 +1,14 @@
+import { ClassNames } from '../interfaces/class-names';
+import WrappedElement from './wrapped-element';
+export default class WrappedInput extends WrappedElement {
+    element: HTMLInputElement;
+    delimiter: string;
+    constructor({ element, classNames, delimiter, }: {
+        element: HTMLInputElement;
+        classNames: ClassNames;
+        delimiter: string;
+    });
+    get value(): string;
+    set value(value: string);
+}
+//# sourceMappingURL=wrapped-input.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..224197b86762368e0f5199c284a9749e20a81ee6
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wrapped-input.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,cAAc;IACtD,OAAO,EAAE,gBAAgB,CAAC;IAE1B,SAAS,EAAE,MAAM,CAAC;gBAEN,EACV,OAAO,EACP,UAAU,EACV,SAAS,GACV,EAAE;QACD,OAAO,EAAE,gBAAgB,CAAC;QAC1B,UAAU,EAAE,UAAU,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB;IAKD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;CACF"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5b233465aa9c838adc720f71f1ef8089eefd5928
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=wrapped-input.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..28ba6ce03e7a6174d57fa9a8772aae36bb191855
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-input.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wrapped-input.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-input.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5b4f647b8436383165c775b9e26eb84aea33e665
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.d.ts
@@ -0,0 +1,19 @@
+import { ClassNames } from '../interfaces/class-names';
+import { Item } from '../interfaces/item';
+import WrappedElement from './wrapped-element';
+export default class WrappedSelect extends WrappedElement {
+    element: HTMLSelectElement;
+    classNames: ClassNames;
+    template: (data: object) => HTMLOptionElement;
+    constructor({ element, classNames, template, }: {
+        element: HTMLSelectElement;
+        classNames: ClassNames;
+        template: (data: object) => HTMLOptionElement;
+    });
+    get placeholderOption(): HTMLOptionElement | null;
+    get optionGroups(): Element[];
+    get options(): Item[] | HTMLOptionElement[];
+    set options(options: Item[] | HTMLOptionElement[]);
+    appendDocFragment(fragment: DocumentFragment): void;
+}
+//# sourceMappingURL=wrapped-select.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..47ebbf79b1d1248b40e1f505595506b668610a0f
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wrapped-select.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,cAAc;IACvD,OAAO,EAAE,iBAAiB,CAAC;IAE3B,UAAU,EAAE,UAAU,CAAC;IAEvB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,iBAAiB,CAAC;gBAElC,EACV,OAAO,EACP,UAAU,EACV,QAAQ,GACT,EAAE;QACD,OAAO,EAAE,iBAAiB,CAAC;QAC3B,UAAU,EAAE,UAAU,CAAC;QACvB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,iBAAiB,CAAC;KAC/C;IAKD,IAAI,iBAAiB,IAAI,iBAAiB,GAAG,IAAI,CAMhD;IAED,IAAI,YAAY,IAAI,OAAO,EAAE,CAE5B;IAED,IAAI,OAAO,IAAI,IAAI,EAAE,GAAG,iBAAiB,EAAE,CAE1C;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,EAahD;IAED,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;CAIpD"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..414d6156e50cb8a17329228403f0feb43bfbd147
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=wrapped-select.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..e9740956186e99435f839e4ca5b7b7e6a82a4df5
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/components/wrapped-select.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wrapped-select.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-select.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/constants.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/constants.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1891ccca285d75912c16808d8e8f6916588f1d22
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/constants.d.ts
@@ -0,0 +1,11 @@
+import { ActionType } from './interfaces/action-type';
+import { EventType } from './interfaces/event-type';
+import { KeyCodeMap } from './interfaces/keycode-map';
+export declare const EVENTS: Record<EventType, EventType>;
+export declare const ACTION_TYPES: Record<ActionType, ActionType>;
+export declare const KEY_CODES: KeyCodeMap;
+export declare const TEXT_TYPE: HTMLInputElement['type'];
+export declare const SELECT_ONE_TYPE: HTMLSelectElement['type'];
+export declare const SELECT_MULTIPLE_TYPE: HTMLSelectElement['type'];
+export declare const SCROLLING_SPEED = 4;
+//# sourceMappingURL=constants.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/constants.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/constants.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..1a564df7597fa080ec66c876c2e606ea4e1c9325
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/constants.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/scripts/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,CAW/C,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,CAYvD,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,UAUvB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,gBAAgB,CAAC,MAAM,CAAU,CAAC;AAC1D,eAAO,MAAM,eAAe,EAAE,iBAAiB,CAAC,MAAM,CAAgB,CAAC;AACvE,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,CAAC,MAAM,CACxC,CAAC;AAEpB,eAAO,MAAM,eAAe,IAAI,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/constants.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/constants.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bc2a88b9f0617820da029b93bd0913ca1f7ed400
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/constants.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=constants.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/constants.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/constants.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..20f7799788b5aab4e1ebefeec405a3483b06d168
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/constants.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"constants.test.d.ts","sourceRoot":"","sources":["../../../../src/scripts/constants.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/defaults.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/defaults.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..db8a9a92fb8b3709d6bc4978de9b70b5be9e091b
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/defaults.d.ts
@@ -0,0 +1,5 @@
+import { ClassNames } from './interfaces/class-names';
+import { Options } from './interfaces/options';
+export declare const DEFAULT_CLASSNAMES: ClassNames;
+export declare const DEFAULT_CONFIG: Options;
+//# sourceMappingURL=defaults.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/defaults.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/defaults.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..dffe8a888c1d62cf9454f0e5344e02bc9eb01b27
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/defaults.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../../src/scripts/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,eAAO,MAAM,kBAAkB,EAAE,UA2BhC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,OA+C5B,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/action-type.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/action-type.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7d694f578488ff865c885851227e2b396c60387
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/action-type.d.ts
@@ -0,0 +1,2 @@
+export type ActionType = 'ADD_CHOICE' | 'FILTER_CHOICES' | 'ACTIVATE_CHOICES' | 'CLEAR_CHOICES' | 'ADD_GROUP' | 'ADD_ITEM' | 'REMOVE_ITEM' | 'HIGHLIGHT_ITEM' | 'CLEAR_ALL' | 'RESET_TO' | 'SET_IS_LOADING';
+//# sourceMappingURL=action-type.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/action-type.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/action-type.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..9721610aac638f6c13053e883ebd438d24bb8cd1
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/action-type.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"action-type.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/action-type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,WAAW,GACX,UAAU,GACV,aAAa,GACb,gBAAgB,GAChB,WAAW,GACX,UAAU,GACV,gBAAgB,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choice.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choice.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..db222d054541b41e88eee3926f9e413f8ae2162f
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choice.d.ts
@@ -0,0 +1,16 @@
+export interface Choice {
+    id?: number;
+    customProperties?: Record<string, any>;
+    disabled?: boolean;
+    active?: boolean;
+    elementId?: number;
+    groupId?: number;
+    keyCode?: number;
+    label: string;
+    placeholder?: boolean;
+    selected?: boolean;
+    value: any;
+    score?: number;
+    choices?: Choice[];
+}
+//# sourceMappingURL=choice.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choice.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choice.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..b692cb7af6dce3122c1b739b58fa11e56bcfc10c
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choice.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"choice.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/choice.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choices.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choices.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..613a2ab0d81c7f82449f5fa2f7475815b11299d4
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choices.d.ts
@@ -0,0 +1,46 @@
+import { Options } from 'deepmerge';
+import { Store } from 'redux';
+import { WrappedInput, WrappedSelect, Container, List, Input, Dropdown } from '../components';
+import { Choice } from './choice';
+import { Group } from './group';
+import { Item } from './item';
+import { State } from './state';
+import templates from '../templates';
+export interface Choices {
+    initialised: boolean;
+    config: Options;
+    passedElement: WrappedInput | WrappedSelect;
+    containerOuter: Container;
+    containerInner: Container;
+    choiceList: List;
+    itemList: List;
+    input: Input;
+    dropdown: Dropdown;
+    _isTextElement: boolean;
+    _isSelectOneElement: boolean;
+    _isSelectMultipleElement: boolean;
+    _isSelectElement: boolean;
+    _store: Store;
+    _templates: typeof templates;
+    _initialState: State;
+    _currentState: State;
+    _prevState: State;
+    _currentValue: string;
+    _canSearch: boolean;
+    _isScrollingOnIe: boolean;
+    _highlightPosition: number;
+    _wasTap: boolean;
+    _isSearching: boolean;
+    _placeholderValue: string | null;
+    _baseId: string;
+    _direction: HTMLElement['dir'];
+    _idNames: {
+        itemChoice: string;
+    };
+    _presetGroups: Group[] | HTMLOptGroupElement[] | Element[];
+    _presetOptions: Item[] | HTMLOptionElement[];
+    _presetChoices: Partial<Choice>[];
+    _presetItems: Item[] | string[];
+    new (element: string | Element | HTMLInputElement | HTMLSelectElement, userConfig: Partial<Options>): any;
+}
+//# sourceMappingURL=choices.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choices.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choices.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..acb6e0859ea2272d5a2e958e78ab3135a8b91266
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/choices.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"choices.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/choices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EACL,YAAY,EACZ,aAAa,EACb,SAAS,EACT,IAAI,EACJ,KAAK,EACL,QAAQ,EACT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,SAAS,MAAM,cAAc,CAAC;AAErC,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAEhB,aAAa,EAAE,YAAY,GAAG,aAAa,CAAC;IAE5C,cAAc,EAAE,SAAS,CAAC;IAE1B,cAAc,EAAE,SAAS,CAAC;IAE1B,UAAU,EAAE,IAAI,CAAC;IAEjB,QAAQ,EAAE,IAAI,CAAC;IAEf,KAAK,EAAE,KAAK,CAAC;IAEb,QAAQ,EAAE,QAAQ,CAAC;IAEnB,cAAc,EAAE,OAAO,CAAC;IAExB,mBAAmB,EAAE,OAAO,CAAC;IAE7B,wBAAwB,EAAE,OAAO,CAAC;IAElC,gBAAgB,EAAE,OAAO,CAAC;IAE1B,MAAM,EAAE,KAAK,CAAC;IAEd,UAAU,EAAE,OAAO,SAAS,CAAC;IAE7B,aAAa,EAAE,KAAK,CAAC;IAErB,aAAa,EAAE,KAAK,CAAC;IAErB,UAAU,EAAE,KAAK,CAAC;IAElB,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;IAEpB,gBAAgB,EAAE,OAAO,CAAC;IAE1B,kBAAkB,EAAE,MAAM,CAAC;IAE3B,OAAO,EAAE,OAAO,CAAC;IAEjB,YAAY,EAAE,OAAO,CAAC;IAEtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC,OAAO,EAAE,MAAM,CAAC;IAEhB,UAAU,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IAE/B,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,aAAa,EAAE,KAAK,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,EAAE,CAAC;IAE3D,cAAc,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAE7C,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IAElC,YAAY,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;IAEhC,KACE,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,gBAAgB,GAAG,iBAAiB,EAChE,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,OAC5B;CACH"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/class-names.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/class-names.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..09818ef6b8707a4e3412cd56ca388080cb9a6703
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/class-names.d.ts
@@ -0,0 +1,56 @@
+/** Classes added to HTML generated by  By default classnames follow the BEM notation. */
+export interface ClassNames {
+    /** @default 'choices' */
+    containerOuter: string;
+    /** @default 'choices__inner' */
+    containerInner: string;
+    /** @default 'choices__input' */
+    input: string;
+    /** @default 'choices__input--cloned' */
+    inputCloned: string;
+    /** @default 'choices__list' */
+    list: string;
+    /** @default 'choices__list--multiple' */
+    listItems: string;
+    /** @default 'choices__list--single' */
+    listSingle: string;
+    /** @default 'choices__list--dropdown' */
+    listDropdown: string;
+    /** @default 'choices__item' */
+    item: string;
+    /** @default 'choices__item--selectable' */
+    itemSelectable: string;
+    /** @default 'choices__item--disabled' */
+    itemDisabled: string;
+    /** @default 'choices__item--choice' */
+    itemChoice: string;
+    /** @default 'choices__placeholder' */
+    placeholder: string;
+    /** @default 'choices__group' */
+    group: string;
+    /** @default 'choices__heading' */
+    groupHeading: string;
+    /** @default 'choices__button' */
+    button: string;
+    /** @default 'is-active' */
+    activeState: string;
+    /** @default 'is-focused' */
+    focusState: string;
+    /** @default 'is-open' */
+    openState: string;
+    /** @default 'is-disabled' */
+    disabledState: string;
+    /** @default 'is-highlighted' */
+    highlightedState: string;
+    /** @default 'is-selected' */
+    selectedState: string;
+    /** @default 'is-flipped' */
+    flippedState: string;
+    /** @default 'is-loading' */
+    loadingState: string;
+    /** @default 'has-no-results' */
+    noResults: string;
+    /** @default 'has-no-choices' */
+    noChoices: string;
+}
+//# sourceMappingURL=class-names.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/class-names.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/class-names.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..b5c60687842c454a59d3dec0cefaf6a5017adbe5
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/class-names.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"class-names.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/class-names.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,WAAW,UAAU;IACzB,yBAAyB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/event-type.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/event-type.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b2d97ac9b83482b7f9ad7596774371c76bee1e29
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/event-type.d.ts
@@ -0,0 +1,2 @@
+export type EventType = 'addItem' | 'removeItem' | 'highlightItem' | 'unhighlightItem' | 'choice' | 'change' | 'search' | 'showDropdown' | 'hideDropdown' | 'highlightChoice';
+//# sourceMappingURL=event-type.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/event-type.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/event-type.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..4ce8a6fcc199560cb8ab48fc897b36e4de4300ef
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/event-type.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"event-type.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/event-type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,YAAY,GACZ,eAAe,GACf,iBAAiB,GACjB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,cAAc,GACd,iBAAiB,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/group.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/group.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3fa395aacd8d75e20f2a491fc68e8f9ef50fe40d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/group.d.ts
@@ -0,0 +1,7 @@
+export interface Group {
+    id?: number;
+    active?: boolean;
+    disabled?: boolean;
+    value: any;
+}
+//# sourceMappingURL=group.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/group.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/group.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..bff19fb8306daec7b52eaeeed854b0ab6b20b37d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/group.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/group.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,KAAK;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC;CACZ"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/index.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/index.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..66ecddcfa03148aebd491dd9b20c0e3d3a239488
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/index.d.ts
@@ -0,0 +1,16 @@
+export * from './action-type';
+export * from './choice';
+export * from './choices';
+export * from './class-names';
+export * from './event-type';
+export * from './group';
+export * from './item';
+export * from './keycode-map';
+export * from './notice';
+export * from './options';
+export * from './passed-element';
+export * from './passed-element-type';
+export * from './position-options-type';
+export * from './state';
+export * from './types';
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/index.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/index.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..d28ff02d514417da253d3030f410174b36461775
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/item.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/item.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1d58b80a7deba23180e4efe328f7135ab701516e
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/item.d.ts
@@ -0,0 +1,6 @@
+import { Choice } from './choice';
+export interface Item extends Choice {
+    choiceId?: number;
+    highlighted?: boolean;
+}
+//# sourceMappingURL=item.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/item.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/item.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..15f89f15103c1423cb8c38b6aa4640a762e92139
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/item.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,IAAK,SAAQ,MAAM;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/keycode-map.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/keycode-map.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f7ece2bcf8c4e76aedd36ea50d8b1943752b8d33
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/keycode-map.d.ts
@@ -0,0 +1,12 @@
+export interface KeyCodeMap {
+    BACK_KEY: 46;
+    DELETE_KEY: 8;
+    ENTER_KEY: 13;
+    A_KEY: 65;
+    ESC_KEY: 27;
+    UP_KEY: 38;
+    DOWN_KEY: 40;
+    PAGE_UP_KEY: 33;
+    PAGE_DOWN_KEY: 34;
+}
+//# sourceMappingURL=keycode-map.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/keycode-map.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/keycode-map.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..04580068d9232fd177fdd5a05b88e46282ac7d8b
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/keycode-map.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"keycode-map.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/keycode-map.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,EAAE,CAAC;IACb,UAAU,EAAE,CAAC,CAAC;IACd,SAAS,EAAE,EAAE,CAAC;IACd,KAAK,EAAE,EAAE,CAAC;IACV,OAAO,EAAE,EAAE,CAAC;IACZ,MAAM,EAAE,EAAE,CAAC;IACX,QAAQ,EAAE,EAAE,CAAC;IACb,WAAW,EAAE,EAAE,CAAC;IAChB,aAAa,EAAE,EAAE,CAAC;CACnB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/notice.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/notice.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..071f8d43c39adb2973f1fe78892290689b2b1351
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/notice.d.ts
@@ -0,0 +1,5 @@
+export interface Notice {
+    response: boolean;
+    notice: string;
+}
+//# sourceMappingURL=notice.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/notice.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/notice.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..f2983f3a10e5976944abba38708118ca7fcb8961
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/notice.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"notice.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/notice.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/options.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/options.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0952366a5e29cc1e9c67460c4a3c493cd6024dd8
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/options.d.ts
@@ -0,0 +1,464 @@
+import Fuse from 'fuse.js';
+import { Choices } from './choices';
+import { Choice } from './choice';
+import { ClassNames } from './class-names';
+import { PositionOptionsType } from './position-options-type';
+import { Types } from './types';
+/**
+ * Choices options interface
+ *
+ * **Terminology**
+ *
+ * - **Choice:** A choice is a value a user can select. A choice would be equivalent to the `<option></option>` element within a select input.
+ * - **Group:** A group is a collection of choices. A group should be seen as equivalent to a `<optgroup></optgroup>` element within a select input.
+ * - **Item:** An item is an inputted value **_(text input)_** or a selected choice **_(select element)_**. In the context of a select element, an item is equivelent to a selected option element: `<option value="Hello" selected></option>` whereas in the context of a text input an item is equivelant to `<input type="text" value="Hello">`
+ */
+export interface Options {
+    /**
+     * Optionally suppress console errors and warnings.
+     *
+     * **Input types affected:** text, select-single, select-multiple
+     *
+     * @default false
+     */
+    silent: boolean;
+    /**
+     * Add pre-selected items (see terminology) to text input.
+     *
+     * **Input types affected:** text
+     *
+     * @example
+     * ```
+     * ['value 1', 'value 2', 'value 3']
+     * ```
+     *
+     * @example
+     * ```
+     * [{
+     *    value: 'Value 1',
+     *    label: 'Label 1',
+     *    id: 1
+     *  },
+     *  {
+     *    value: 'Value 2',
+     *    label: 'Label 2',
+     *    id: 2,
+     *    customProperties: {
+     *      random: 'I am a custom property'
+     *  }
+     * }]
+     * ```
+     *
+     * @default []
+     */
+    items: string[] | Choice[];
+    /**
+     * Add choices (see terminology) to select input.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @example
+     * ```
+     * [{
+     *   value: 'Option 1',
+     *   label: 'Option 1',
+     *   selected: true,
+     *   disabled: false,
+     * },
+     * {
+     *   value: 'Option 2',
+     *   label: 'Option 2',
+     *   selected: false,
+     *   disabled: true,
+     *   customProperties: {
+     *     description: 'Custom description about Option 2',
+     *     random: 'Another random custom property'
+     *   },
+     * }]
+     * ```
+     *
+     * @default []
+     */
+    choices: Choice[];
+    /**
+     * The amount of choices to be rendered within the dropdown list `("-1" indicates no limit)`. This is useful if you have a lot of choices where it is easier for a user to use the search area to find a choice.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default -1
+     */
+    renderChoiceLimit: number;
+    /**
+     * The amount of items a user can input/select `("-1" indicates no limit)`.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * @default -1
+     */
+    maxItemCount: number;
+    /**
+     * Whether a user can add items.
+     *
+     * **Input types affected:** text
+     *
+     * @default true
+     */
+    addItems: boolean;
+    /**
+     * A filter that will need to pass for a user to successfully add an item.
+     *
+     * **Input types affected:** text
+     *
+     * @default null
+     */
+    addItemFilter: string | RegExp | Types.FilterFunction | null;
+    /**
+     * The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string.
+     *
+     * **Input types affected:** text
+     *
+     * @default
+     * ```
+     * (value) => `Press Enter to add <b>"${value}"</b>`;
+     * ```
+     */
+    addItemText: string | Types.NoticeStringFunction;
+    /**
+     * Whether a user can remove items.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * @default true
+     */
+    removeItems: boolean;
+    /**
+     * Whether each item should have a remove button.
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * @default false
+     */
+    removeItemButton: boolean;
+    /**
+     * Whether a user can edit items. An item's value can be edited by pressing the backspace.
+     *
+     * **Input types affected:** text
+     *
+     * @default false
+     */
+    editItems: boolean;
+    /**
+     * Whether HTML should be rendered in all Choices elements.
+     * If `false`, all elements (placeholder, items, etc.) will be treated as plain text.
+     * If `true`, this can be used to perform XSS scripting attacks if you load choices from a remote source.
+     *
+     * **Deprecation Warning:** This will default to `false` in a future release.
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * @default true
+     */
+    allowHTML: boolean;
+    /**
+     * Whether each inputted/chosen item should be unique.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * @default true
+     */
+    duplicateItemsAllowed: boolean;
+    /**
+     * What divides each value. The default delimiter separates each value with a comma: `"Value 1, Value 2, Value 3"`.
+     *
+     * **Input types affected:** text
+     *
+     * @default ','
+     */
+    delimiter: string;
+    /**
+     * Whether a user can paste into the input.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * @default true
+     */
+    paste: boolean;
+    /**
+     * Whether a search area should be shown.
+     *
+     * @note Multiple select boxes will always show search areas.
+     *
+     * **Input types affected:** select-one
+     *
+     * @default true
+     */
+    searchEnabled: boolean;
+    /**
+     * Whether choices should be filtered by input or not. If `false`, the search event will still emit, but choices will not be filtered.
+     *
+     * **Input types affected:** select-one
+     *
+     * @default true
+     */
+    searchChoices: boolean;
+    /**
+     * The minimum length a search value should be before choices are searched.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default 1
+     */
+    searchFloor: number;
+    /**
+     * The maximum amount of search results to show.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default 4
+     */
+    searchResultLimit: number;
+    /**
+     * Specify which fields should be used when a user is searching. If you have added custom properties to your choices, you can add these values thus: `['label', 'value', 'customProperties.example']`.
+     *
+     * Input types affected:select-one, select-multiple
+     *
+     * @default ['label', 'value']
+     */
+    searchFields: string[];
+    /**
+     * Whether the dropdown should appear above `(top)` or below `(bottom)` the input. By default, if there is not enough space within the window the dropdown will appear above the input, otherwise below it.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default 'auto'
+     */
+    position: PositionOptionsType;
+    /**
+     * Whether the scroll position should reset after adding an item.
+     *
+     * **Input types affected:** select-multiple
+     *
+     * @default true
+     */
+    resetScrollPosition: boolean;
+    /**
+     * Whether choices and groups should be sorted. If false, choices/groups will appear in the order they were given.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default true
+     */
+    shouldSort: boolean;
+    /**
+     * Whether items should be sorted. If false, items will appear in the order they were selected.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * @default false
+     */
+    shouldSortItems: boolean;
+    /**
+     * The function that will sort choices and items before they are displayed (unless a user is searching). By default choices and items are sorted by alphabetical order.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @example
+     * ```
+     * // Sorting via length of label from largest to smallest
+     * const example = new Choices(element, {
+     *   sorter: function(a, b) {
+     *     return b.label.length - a.label.length;
+     *   },
+     * };
+     * ```
+     *
+     * @default sortByAlpha
+     */
+    sorter: (current: Choice, next: Choice) => number;
+    /**
+     * Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * @note For single select boxes, the recommended way of adding a placeholder is as follows:
+     * ```
+     * <select>
+     *   <option placeholder>This is a placeholder</option>
+     *   <option>...</option>
+     *   <option>...</option>
+     *   <option>...</option>
+     * </select>
+     * ```
+     *
+     * @default true
+     */
+    placeholder: boolean;
+    /**
+     * The value of the inputs placeholder.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * @default null
+     */
+    placeholderValue: string | null;
+    /**
+     * The value of the search inputs placeholder.
+     *
+     * **Input types affected:** select-one
+     *
+     * @default null
+     */
+    searchPlaceholderValue: string | null;
+    /**
+     * Prepend a value to each item added/selected.
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * @default null
+     */
+    prependValue: string | null;
+    /**
+     * Append a value to each item added/selected.
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * @default null
+     */
+    appendValue: string | null;
+    /**
+     * Whether selected choices should be removed from the list. By default choices are removed when they are selected in multiple select box. To always render choices pass `always`.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default 'auto';
+     */
+    renderSelectedChoices: 'auto' | 'always';
+    /**
+     * The text that is shown whilst choices are being populated via AJAX.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default 'Loading...'
+     */
+    loadingText: string;
+    /**
+     * The text that is shown when a user's search has returned no results. Optionally pass a function returning a string.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default 'No results found'
+     */
+    noResultsText: string | Types.StringFunction;
+    /**
+     * The text that is shown when a user has selected all possible choices. Optionally pass a function returning a string.
+     *
+     * **Input types affected:** select-multiple
+     *
+     * @default 'No choices to choose from'
+     */
+    noChoicesText: string | Types.StringFunction;
+    /**
+     * The text that is shown when a user hovers over a selectable choice.
+     *
+     * **Input types affected:** select-multiple, select-one
+     *
+     * @default 'Press to select'
+     */
+    itemSelectText: string;
+    /**
+     * The text that is shown when a user has focus on the input but has already reached the **max item count** [https://github.com/jshjohnson/Choices#maxitemcount]. To access the max item count, pass a function with a `maxItemCount` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string.
+     *
+     * **Input types affected:** text
+     *
+     * @default
+     * ```
+     * (maxItemCount) => `Only ${maxItemCount} values can be added.`;
+     * ```
+     */
+    maxItemText: string | Types.NoticeLimitFunction;
+    /**
+     * If no duplicates are allowed, and the value already exists in the array.
+     *
+     * @default 'Only unique values can be added'
+     */
+    uniqueItemText: string | Types.NoticeStringFunction;
+    /**
+     * The text that is shown when addItemFilter is passed and it returns false
+     *
+     * **Input types affected:** text
+     *
+     * @default 'Only values matching specific conditions can be added'
+     */
+    customAddItemText: string | Types.NoticeStringFunction;
+    /**
+     * Compare choice and value in appropriate way (e.g. deep equality for objects). To compare choice and value, pass a function with a `valueComparer` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example).
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * @default
+     * ```
+     * (choice, item) => choice === item;
+     * ```
+     */
+    valueComparer: Types.ValueCompareFunction;
+    /**
+     * Classes added to HTML generated by  By default classnames follow the BEM notation.
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     */
+    classNames: ClassNames;
+    /**
+     * Choices uses the great Fuse library for searching. You can find more options here: https://fusejs.io/api/options.html
+     */
+    fuseOptions: Fuse.IFuseOptions<Choices>;
+    /**
+     * ID of the connected label to improve a11y. If set, aria-labelledby will be added.
+     */
+    labelId: string;
+    /**
+     * Function to run once Choices initialises.
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * @note For each callback, this refers to the current instance of  This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`.
+     *
+     * @default null
+     */
+    callbackOnInit: ((this: Choices) => void) | null;
+    /**
+     * Function to run on template creation. Through this callback it is possible to provide custom templates for the various components of Choices (see terminology). For Choices to work with custom templates, it is important you maintain the various data attributes defined here [https://github.com/jshjohnson/Choices/blob/67f29c286aa21d88847adfcd6304dc7d068dc01f/assets/scripts/src/choices.js#L1993-L2067].
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * @note For each callback, this refers to the current instance of  This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`.
+     *
+     * @example
+     * ```
+     * const example = new Choices(element, {
+     *   callbackOnCreateTemplates: function (template) {
+     *     var classNames = this.config.classNames;
+     *     return {
+     *       item: (data) => {
+     *         return template(`
+     *           <div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : classNames.itemSelectable}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''}>
+     *             <span>&bigstar;</span> ${data.label}
+     *           </div>
+     *         `);
+     *       },
+     *       choice: (data) => {
+     *         return template(`
+     *           <div class="${classNames.item} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId > 0 ? 'role="treeitem"' : 'role="option"'}>
+     *             <span>&bigstar;</span> ${data.label}
+     *           </div>
+     *         `);
+     *       },
+     *     };
+     *   }
+     * });
+     * ```
+     *
+     * @default null
+     */
+    callbackOnCreateTemplates: ((template: Types.StrToEl) => void) | null;
+}
+//# sourceMappingURL=options.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/options.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/options.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..7d1f58d58209febb6e9ea10a792de37a30f5749e
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/options.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/options.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC;;;;;;;;GAQG;AACH,MAAM,WAAW,OAAO;IACtB;;;;;;OAMG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;;;OAMG;IACH,QAAQ,EAAE,OAAO,CAAC;IAElB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;IAE7D;;;;;;;;;OASG;IACH,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,oBAAoB,CAAC;IAEjD;;;;;;OAMG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;;;;;OAMG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAE1B;;;;;;OAMG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;;;;;;;;;OAUG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAE/B;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;OAMG;IACH,KAAK,EAAE,OAAO,CAAC;IAEf;;;;;;;;OAQG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;;;;;OAMG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;IAEvB;;;;;;OAMG;IACH,QAAQ,EAAE,mBAAmB,CAAC;IAE9B;;;;;;OAMG;IACH,mBAAmB,EAAE,OAAO,CAAC;IAE7B;;;;;;OAMG;IACH,UAAU,EAAE,OAAO,CAAC;IAEpB;;;;;;OAMG;IACH,eAAe,EAAE,OAAO,CAAC;IAEzB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAElD;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;;;OAMG;IACH,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtC;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B;;;;;;OAMG;IACH,qBAAqB,EAAE,MAAM,GAAG,QAAQ,CAAC;IAEzC;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;IAE7C;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;IAE7C;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;;;;;;;;OASG;IACH,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,mBAAmB,CAAC;IAEhD;;;;OAIG;IACH,cAAc,EAAE,MAAM,GAAG,KAAK,CAAC,oBAAoB,CAAC;IAEpD;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,GAAG,KAAK,CAAC,oBAAoB,CAAC;IAEvD;;;;;;;;;OASG;IACH,aAAa,EAAE,KAAK,CAAC,oBAAoB,CAAC;IAE1C;;;;OAIG;IACH,UAAU,EAAE,UAAU,CAAC;IAEvB;;OAEG;IACH,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAExC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;;;;OAQG;IACH,cAAc,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,yBAAyB,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CACvE"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element-type.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element-type.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..19ca5068273376b04ea4056913a79da972634390
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element-type.d.ts
@@ -0,0 +1,2 @@
+export type PassedElementType = 'text' | 'select-one' | 'select-multiple';
+//# sourceMappingURL=passed-element-type.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element-type.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element-type.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..e49017369056f27c19a99d61b0de433ab3d5f746
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element-type.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"passed-element-type.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/passed-element-type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,YAAY,GAAG,iBAAiB,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..54172d70bb47b0d5c267aa6a672b40834df9cdb8
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element.d.ts
@@ -0,0 +1,129 @@
+import { Choices } from './choices';
+import { Choice } from './choice';
+import { ClassNames } from './class-names';
+import { EventType } from './event-type';
+import { PassedElementType } from './passed-element-type';
+export interface PassedElement extends HTMLElement {
+    classNames: ClassNames;
+    element: (HTMLInputElement | HTMLSelectElement) & {
+        addEventListener<K extends EventType>(type: K, listener: (this: HTMLInputElement | HTMLSelectElement, ev: EventMap[K]) => void, options?: boolean | AddEventListenerOptions): void;
+    };
+    type: PassedElementType;
+    isDisabled: boolean;
+    parentInstance: Choices;
+}
+/**
+ * Events fired by Choices behave the same as standard events. Each event is triggered on the element passed to Choices (accessible via `this.passedElement`. Arguments are accessible within the `event.detail` object.
+ */
+export interface EventMap {
+    /**
+     * Triggered each time an item is added (programmatically or by the user).
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * Arguments: id, value, label, groupValue, keyCode
+     */
+    addItem: CustomEvent<{
+        id: number;
+        value: string;
+        label: string;
+        groupValue: string;
+        keyCode: number;
+    }>;
+    /**
+     * Triggered each time an item is removed (programmatically or by the user).
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * Arguments: id, value, label, groupValue
+     */
+    removeItem: CustomEvent<{
+        id: number;
+        value: string;
+        label: string;
+        groupValue: string;
+    }>;
+    /**
+     * Triggered each time an item is highlighted.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * Arguments: id, value, label, groupValue
+     */
+    highlightItem: CustomEvent<{
+        id: number;
+        value: string;
+        label: string;
+        groupValue: string;
+    }>;
+    /**
+     * Triggered each time an item is unhighlighted.
+     *
+     * **Input types affected:** text, select-multiple
+     *
+     * Arguments: id, value, label, groupValue
+     */
+    unhighlightItem: CustomEvent<{
+        id: number;
+        value: string;
+        label: string;
+        groupValue: string;
+    }>;
+    /**
+     * Triggered each time a choice is selected **by a user**, regardless if it changes the value of the input.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * Arguments: choice: Choice
+     */
+    choice: CustomEvent<{
+        choice: Choice;
+    }>;
+    /**
+     * Triggered each time an item is added/removed **by a user**.
+     *
+     * **Input types affected:** text, select-one, select-multiple
+     *
+     * Arguments: value
+     */
+    change: CustomEvent<{
+        value: string;
+    }>;
+    /**
+     * Triggered when a user types into an input to search choices.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * Arguments: value, resultCount
+     */
+    search: CustomEvent<{
+        value: string;
+        resultCount: number;
+    }>;
+    /**
+     * Triggered when the dropdown is shown.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * Arguments: -
+     */
+    showDropdown: CustomEvent<undefined>;
+    /**
+     * Triggered when the dropdown is hidden.
+     *
+     * **Input types affected:** select-one, select-multiple
+     *
+     * Arguments: -
+     */
+    hideDropdown: CustomEvent<undefined>;
+    /**
+     * Triggered when a choice from the dropdown is highlighted.
+     *
+     * Input types affected: select-one, select-multiple
+     * Arguments: el is the choice.passedElement that was affected.
+     */
+    highlightChoice: CustomEvent<{
+        el: PassedElement;
+    }>;
+}
+//# sourceMappingURL=passed-element.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..4798e53f4cdd3b089547496560467227a7491d7b
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/passed-element.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"passed-element.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/passed-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,CAAC,gBAAgB,GAAG,iBAAiB,CAAC,GAAG;QAEhD,gBAAgB,CAAC,CAAC,SAAS,SAAS,EAClC,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CACR,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,EAC1C,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,KACZ,IAAI,EACT,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;KACT,CAAC;IACF,IAAI,EAAE,iBAAiB,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;;;;;OAMG;IACH,OAAO,EAAE,WAAW,CAAC;QACnB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,UAAU,EAAE,WAAW,CAAC;QACtB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,aAAa,EAAE,WAAW,CAAC;QACzB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,eAAe,EAAE,WAAW,CAAC;QAC3B,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,MAAM,EAAE,WAAW,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAExC;;;;;;OAMG;IACH,MAAM,EAAE,WAAW,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEvC;;;;;;OAMG;IACH,MAAM,EAAE,WAAW,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE5D;;;;;;OAMG;IACH,YAAY,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IAErC;;;;;;OAMG;IACH,YAAY,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IAErC;;;;;OAKG;IACH,eAAe,EAAE,WAAW,CAAC;QAAE,EAAE,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;CACrD"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/position-options-type.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/position-options-type.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..694e3d26e453a7bee0fa8600b9d5854b1e453b3f
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/position-options-type.d.ts
@@ -0,0 +1,2 @@
+export type PositionOptionsType = 'auto' | 'top' | 'bottom';
+//# sourceMappingURL=position-options-type.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/position-options-type.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/position-options-type.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..f89c21214ad73c11e87985b86ef69d92c8354519
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/position-options-type.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"position-options-type.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/position-options-type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/state.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/state.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a713d05050c11c2ea2847f6d57449a697bac3543
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/state.d.ts
@@ -0,0 +1,10 @@
+import { Choice } from './choice';
+import { Group } from './group';
+import { Item } from './item';
+export interface State {
+    choices: Choice[];
+    groups: Group[];
+    items: Item[];
+    loading: boolean;
+}
+//# sourceMappingURL=state.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/state.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/state.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..0907396d13ffce354920386f416210b9d49da958
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/state.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/types.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/types.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a2cf64ff0ca2a7c7b592bb2961eb184a49dfa85
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/types.d.ts
@@ -0,0 +1,9 @@
+export declare namespace Types {
+    type StrToEl = (str: string) => HTMLElement | HTMLInputElement | HTMLOptionElement;
+    type StringFunction = () => string;
+    type NoticeStringFunction = (value: string) => string;
+    type NoticeLimitFunction = (maxItemCount: number) => string;
+    type FilterFunction = (value: string) => boolean;
+    type ValueCompareFunction = (value1: string, value2: string) => boolean;
+}
+//# sourceMappingURL=types.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/types.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/types.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..4c44c1d1ec2dd01e17d8b39b1c36e21fdeb084e2
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/interfaces/types.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/types.ts"],"names":[],"mappings":"AAAA,yBAAiB,KAAK,CAAC;IACrB,KAAY,OAAO,GAAG,CACpB,GAAG,EAAE,MAAM,KACR,WAAW,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;IACxD,KAAY,cAAc,GAAG,MAAM,MAAM,CAAC;IAC1C,KAAY,oBAAoB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7D,KAAY,mBAAmB,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACnE,KAAY,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACxD,KAAY,oBAAoB,GAAG,CACjC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,KACX,OAAO,CAAC;CACd"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3eff9ed81490b91873bff42c0f1e0944f4f24d18
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.d.ts
@@ -0,0 +1,28 @@
+import { Choice } from '../interfaces/choice';
+import { EventType } from '../interfaces/event-type';
+export declare const getRandomNumber: (min: number, max: number) => number;
+export declare const generateChars: (length: number) => string;
+export declare const generateId: (element: HTMLInputElement | HTMLSelectElement, prefix: string) => string;
+export declare const getType: (obj: any) => string;
+export declare const isType: (type: string, obj: any) => boolean;
+export declare const wrap: (element: HTMLElement, wrapper?: HTMLElement) => HTMLElement;
+export declare const getAdjacentEl: (startEl: Element, selector: string, direction?: number) => Element;
+export declare const isScrolledIntoView: (element: HTMLElement, parent: HTMLElement, direction?: number) => boolean;
+export declare const sanitise: <T>(value: string | T) => string | T;
+export declare const strToEl: (str: string) => Element;
+interface RecordToCompare {
+    value: string;
+    label?: string;
+}
+export declare const sortByAlpha: ({ value, label }: RecordToCompare, { value: value2, label: label2 }: RecordToCompare) => number;
+export declare const sortByScore: (a: Pick<Choice, 'score'>, b: Pick<Choice, 'score'>) => number;
+export declare const dispatchEvent: (element: HTMLElement, type: EventType, customArgs?: object | null) => boolean;
+export declare const existsInArray: (array: any[], value: string, key?: string) => boolean;
+export declare const cloneObject: (obj: object) => object;
+/**
+ * Returns an array of keys present on the first but missing on the second object
+ */
+export declare const diff: (a: Record<string, any>, b: Record<string, any>) => string[];
+export declare const parseCustomProperties: (customProperties: any) => any;
+export {};
+//# sourceMappingURL=utils.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..7c1131e2974afb246d7ec277ab37d7ba40050fc9
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/lib/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,eAAO,MAAM,eAAe,QAAS,MAAM,OAAO,MAAM,KAAG,MACZ,CAAC;AAEhD,eAAO,MAAM,aAAa,WAAY,MAAM,KAAG,MAC6B,CAAC;AAE7E,eAAO,MAAM,UAAU,YACZ,gBAAgB,GAAG,iBAAiB,UACrC,MAAM,KACb,MASF,CAAC;AAEF,eAAO,MAAM,OAAO,QAAS,GAAG,KAAG,MACe,CAAC;AAEnD,eAAO,MAAM,MAAM,SAAU,MAAM,OAAO,GAAG,KAAG,OACY,CAAC;AAE7D,eAAO,MAAM,IAAI,YACN,WAAW,YACX,WAAW,KACnB,WAUF,CAAC;AAEF,eAAO,MAAM,aAAa,YACf,OAAO,YACN,MAAM,yBAEf,OAYF,CAAC;AAEF,eAAO,MAAM,kBAAkB,YACpB,WAAW,UACZ,WAAW,yBAElB,OAkBF,CAAC;AAEF,eAAO,MAAM,QAAQ,sCAUpB,CAAC;AAEF,eAAO,MAAM,OAAO,QAAe,MAAM,KAAK,OAc1C,CAAC;AAEL,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,eAAO,MAAM,WAAW,qBACI,eAAe,oCACE,eAAe,KACzD,MAKC,CAAC;AAEL,eAAO,MAAM,WAAW,MACnB,KAAK,MAAM,EAAE,OAAO,CAAC,KACrB,KAAK,MAAM,EAAE,OAAO,CAAC,KACvB,MAKF,CAAC;AAEF,eAAO,MAAM,aAAa,YACf,WAAW,QACd,SAAS,eACH,MAAM,GAAG,IAAI,KACxB,OAQF,CAAC;AAEF,eAAO,MAAM,aAAa,UACjB,GAAG,EAAE,SACL,MAAM,mBAEZ,OAOC,CAAC;AAEL,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,MACT,CAAC;AAElC;;GAEG;AACH,eAAO,MAAM,IAAI,MACZ,OAAO,MAAM,EAAE,GAAG,CAAC,KACnB,OAAO,MAAM,EAAE,GAAG,CAAC,KACrB,MAAM,EAKR,CAAC;AAEF,eAAO,MAAM,qBAAqB,6BAAuB,GAUxD,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c727c85cb6955a6f280988d4ce657f6701f3408f
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=utils.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..58a4177e40f8cb6da141e96215bdb3700cb09c1a
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/lib/utils.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/lib/utils.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bf907900a8f7320ba9f52a2d786fdff568427989
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.d.ts
@@ -0,0 +1,8 @@
+import { AddChoiceAction, FilterChoicesAction, ActivateChoicesAction, ClearChoicesAction } from '../actions/choices';
+import { AddItemAction, RemoveItemAction } from '../actions/items';
+import { Choice } from '../interfaces/choice';
+export declare const defaultState: never[];
+type ActionTypes = AddChoiceAction | FilterChoicesAction | ActivateChoicesAction | ClearChoicesAction | AddItemAction | RemoveItemAction | Record<string, never>;
+export default function choices(state?: Choice[], action?: ActionTypes): Choice[];
+export {};
+//# sourceMappingURL=choices.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..efdac490c00346a148c17fd41051cdc7b6dfc433
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"choices.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/choices.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,eAAO,MAAM,YAAY,SAAK,CAAC;AAE/B,KAAK,WAAW,GACZ,eAAe,GACf,mBAAmB,GACnB,qBAAqB,GACrB,kBAAkB,GAClB,aAAa,GACb,gBAAgB,GAChB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE1B,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,KAAK,GAAE,MAAM,EAAiB,EAC9B,MAAM,GAAE,WAAgB,GACvB,MAAM,EAAE,CAwGV"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1b34de17b8e7deae827e04c0b9d2be8057711734
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=choices.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..abcc18fae56ffa5c328e4b95e68a5704173e3503
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/choices.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"choices.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/choices.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c60ba23878a9074f41d63f3e80b89bc094558c93
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.d.ts
@@ -0,0 +1,9 @@
+import { AddGroupAction } from '../actions/groups';
+import { ClearChoicesAction } from '../actions/choices';
+import { Group } from '../interfaces/group';
+import { State } from '../interfaces/state';
+export declare const defaultState: never[];
+type ActionTypes = AddGroupAction | ClearChoicesAction | Record<string, never>;
+export default function groups(state?: Group[], action?: ActionTypes): State['groups'];
+export {};
+//# sourceMappingURL=groups.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..df2d6b5c221c03498d97c40299e38ac17f0b2f22
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/groups.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,eAAO,MAAM,YAAY,SAAK,CAAC;AAE/B,KAAK,WAAW,GAAG,cAAc,GAAG,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/E,MAAM,CAAC,OAAO,UAAU,MAAM,CAC5B,KAAK,GAAE,KAAK,EAAiB,EAC7B,MAAM,GAAE,WAAgB,GACvB,KAAK,CAAC,QAAQ,CAAC,CAwBjB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8d44d56ef8067321d75fd258f67bcd41b36ba371
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=groups.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..ea6a989c1cc8161bd8c660aade52e78ada368afc
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/groups.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"groups.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/groups.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..212bcc132594319149f07447d10392a5069e8ad6
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.d.ts
@@ -0,0 +1,9 @@
+export declare const defaultState: {
+    groups: never[];
+    items: never[];
+    choices: never[];
+    loading: boolean;
+};
+declare const rootReducer: (passedState: any, action: any) => object;
+export default rootReducer;
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..d95e352b21d1170bcb3a9cbf39f27e61a9d061a9
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY;;;;;CAKxB,CAAC;AASF,QAAA,MAAM,WAAW,qCAA0B,MAa1C,CAAC;AAEF,eAAe,WAAW,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..121d59b38b6a3c8e8fabd3b46bebfd27aa041e97
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=index.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..c182b406a838413e040192689d34ffd0734568f7
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/index.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/index.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b825f9d5791e883538688e6c2c6d06d7c1a9e33
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.d.ts
@@ -0,0 +1,8 @@
+import { AddItemAction, RemoveItemAction, HighlightItemAction } from '../actions/items';
+import { Item } from '../interfaces/item';
+import { State } from '../interfaces/state';
+export declare const defaultState: never[];
+type ActionTypes = AddItemAction | RemoveItemAction | HighlightItemAction | Record<string, never>;
+export default function items(state?: Item[], action?: ActionTypes): State['items'];
+export {};
+//# sourceMappingURL=items.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..517343b941edc094009c8ab09ddefe80d03932b5
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"items.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/items.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,eAAO,MAAM,YAAY,SAAK,CAAC;AAE/B,KAAK,WAAW,GACZ,aAAa,GACb,gBAAgB,GAChB,mBAAmB,GACnB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE1B,MAAM,CAAC,OAAO,UAAU,KAAK,CAC3B,KAAK,GAAE,IAAI,EAAiB,EAC5B,MAAM,GAAE,WAAgB,GACvB,KAAK,CAAC,OAAO,CAAC,CA0DhB"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e81d58b44415bfe62423891267a9482477396723
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=items.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..641925713b0c483b55762c73e7f485d95ee4b3b8
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/items.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"items.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/items.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b5518f0eb22941aaed939302a7d58cacfd8904f6
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.d.ts
@@ -0,0 +1,7 @@
+import { SetIsLoadingAction } from '../actions/misc';
+import { State } from '../interfaces/state';
+export declare const defaultState = false;
+type ActionTypes = SetIsLoadingAction | Record<string, never>;
+declare const general: (state?: boolean, action?: ActionTypes) => State['loading'];
+export default general;
+//# sourceMappingURL=loading.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..34945a6093de05952e79d518fd61577358c82aab
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"loading.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/loading.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,eAAO,MAAM,YAAY,QAAQ,CAAC;AAElC,KAAK,WAAW,GAAG,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE9D,QAAA,MAAM,OAAO,6BAEH,WAAW,KAClB,KAAK,CAAC,SAAS,CAUjB,CAAC;AAEF,eAAe,OAAO,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b1d0de1d697c13eac9b7e38a94babb086bd4142a
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=loading.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..1f2efe5ceaf26734df1cd3d50f824e1adbe60ab2
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/reducers/loading.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"loading.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/loading.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/store/store.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/store/store.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b6ff41204250d04073ce90e5420488e8c7cc0cd4
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/store/store.d.ts
@@ -0,0 +1,74 @@
+import { Store as IStore, AnyAction } from 'redux';
+import { Choice } from '../interfaces/choice';
+import { Group } from '../interfaces/group';
+import { Item } from '../interfaces/item';
+import { State } from '../interfaces/state';
+export default class Store {
+    _store: IStore;
+    constructor();
+    /**
+     * Subscribe store to function call (wrapped Redux method)
+     */
+    subscribe(onChange: () => void): void;
+    /**
+     * Dispatch event to store (wrapped Redux method)
+     */
+    dispatch(action: AnyAction): void;
+    /**
+     * Get store object (wrapping Redux method)
+     */
+    get state(): State;
+    /**
+     * Get items from store
+     */
+    get items(): Item[];
+    /**
+     * Get active items from store
+     */
+    get activeItems(): Item[];
+    /**
+     * Get highlighted items from store
+     */
+    get highlightedActiveItems(): Item[];
+    /**
+     * Get choices from store
+     */
+    get choices(): Choice[];
+    /**
+     * Get active choices from store
+     */
+    get activeChoices(): Choice[];
+    /**
+     * Get selectable choices from store
+     */
+    get selectableChoices(): Choice[];
+    /**
+     * Get choices that can be searched (excluding placeholders)
+     */
+    get searchableChoices(): Choice[];
+    /**
+     * Get placeholder choice from store
+     */
+    get placeholderChoice(): Choice | undefined;
+    /**
+     * Get groups from store
+     */
+    get groups(): Group[];
+    /**
+     * Get active groups from store
+     */
+    get activeGroups(): Group[];
+    /**
+     * Get loading state from store
+     */
+    isLoading(): boolean;
+    /**
+     * Get single choice by it's ID
+     */
+    getChoiceById(id: string): Choice | undefined;
+    /**
+     * Get group by group id
+     */
+    getGroupById(id: number): Group | undefined;
+}
+//# sourceMappingURL=store.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/store/store.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/store/store.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..32a519e7b298b693d6771a2961899fd21ad2cf5d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/store/store.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/store/store.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,IAAI,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5C,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,MAAM,EAAE,MAAM,CAAC;;IAUf;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAIrC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAIjC;;OAEG;IACH,IAAI,KAAK,IAAI,KAAK,CAEjB;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,IAAI,EAAE,CAElB;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,IAAI,EAAE,CAExB;IAED;;OAEG;IACH,IAAI,sBAAsB,IAAI,IAAI,EAAE,CAEnC;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,EAAE,CAEtB;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,EAAE,CAE5B;IAED;;OAEG;IACH,IAAI,iBAAiB,IAAI,MAAM,EAAE,CAEhC;IAED;;OAEG;IACH,IAAI,iBAAiB,IAAI,MAAM,EAAE,CAIhC;IAED;;OAEG;IACH,IAAI,iBAAiB,IAAI,MAAM,GAAG,SAAS,CAI1C;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,KAAK,EAAE,CAEpB;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,KAAK,EAAE,CAW1B;IAED;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;OAEG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;CAG5C"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/store/store.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/store/store.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6ffd6f9ed2070f7d6bb0f0ab808d2e753aaa5a20
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/store/store.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=store.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/store/store.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/store/store.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..fa49f4c703d3d0e7715ea7f4c4bdb478084bd9e5
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/store/store.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/store/store.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/templates.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/templates.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..03a5ebfeb1bdf4f0f59aaeec6f5923614c687376
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/templates.d.ts
@@ -0,0 +1,25 @@
+/**
+ * Helpers to create HTML elements used by Choices
+ * Can be overridden by providing `callbackOnCreateTemplates` option
+ */
+import { Choice } from './interfaces/choice';
+import { Group } from './interfaces/group';
+import { Item } from './interfaces/item';
+import { PassedElementType } from './interfaces/passed-element-type';
+type TemplateOptions = Record<'classNames' | 'allowHTML', any>;
+declare const templates: {
+    containerOuter({ classNames: { containerOuter } }: TemplateOptions, dir: HTMLElement['dir'], isSelectElement: boolean, isSelectOneElement: boolean, searchEnabled: boolean, passedElementType: PassedElementType, labelId: string): HTMLDivElement;
+    containerInner({ classNames: { containerInner }, }: TemplateOptions): HTMLDivElement;
+    itemList({ classNames: { list, listSingle, listItems } }: TemplateOptions, isSelectOneElement: boolean): HTMLDivElement;
+    placeholder({ allowHTML, classNames: { placeholder } }: TemplateOptions, value: string): HTMLDivElement;
+    item({ allowHTML, classNames: { item, button, highlightedState, itemSelectable, placeholder, }, }: TemplateOptions, { id, value, label, customProperties, active, disabled, highlighted, placeholder: isPlaceholder, }: Item, removeItemButton: boolean): HTMLDivElement;
+    choiceList({ classNames: { list } }: TemplateOptions, isSelectOneElement: boolean): HTMLDivElement;
+    choiceGroup({ allowHTML, classNames: { group, groupHeading, itemDisabled }, }: TemplateOptions, { id, value, disabled }: Group): HTMLDivElement;
+    choice({ allowHTML, classNames: { item, itemChoice, itemSelectable, selectedState, itemDisabled, placeholder, }, }: TemplateOptions, { id, value, label, groupId, elementId, disabled: isDisabled, selected: isSelected, placeholder: isPlaceholder, }: Choice, selectText: string): HTMLDivElement;
+    input({ classNames: { input, inputCloned } }: TemplateOptions, placeholderValue: string): HTMLInputElement;
+    dropdown({ classNames: { list, listDropdown }, }: TemplateOptions): HTMLDivElement;
+    notice({ allowHTML, classNames: { item, itemChoice, noResults, noChoices }, }: TemplateOptions, innerText: string, type?: 'no-choices' | 'no-results' | ''): HTMLDivElement;
+    option({ label, value, customProperties, active, disabled, }: Item): HTMLOptionElement;
+};
+export default templates;
+//# sourceMappingURL=templates.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/templates.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/templates.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..ac8b7018b4d720147dc0ff9db436669d8e31ff45
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/templates.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../../src/scripts/templates.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAGrE,KAAK,eAAe,GAAG,MAAM,CAAC,YAAY,GAAG,WAAW,EAAE,GAAG,CAAC,CAAC;AAE/D,QAAA,MAAM,SAAS;uDAEyB,eAAe,OAC9C,WAAW,CAAC,KAAK,CAAC,mBACN,OAAO,sBACJ,OAAO,iBACZ,OAAO,qBACH,iBAAiB,WAC3B,MAAM,GACd,cAAc;wDAiCd,eAAe,GAAG,cAAc;8DAOgB,eAAe,sBAC5C,OAAO,GAC1B,cAAc;4DAO6B,eAAe,SACpD,MAAM,GACZ,cAAc;uGAiBZ,eAAe,sGAUf,IAAI,oBACW,OAAO,GACxB,cAAc;yCAmDW,eAAe,sBACrB,OAAO,GAC1B,cAAc;mFAiBZ,eAAe,2BACO,KAAK,GAC7B,cAAc;wHAsCZ,eAAe,qHAUf,MAAM,cACG,MAAM,GACjB,cAAc;kDAqCyB,eAAe,oBACrC,MAAM,GACvB,gBAAgB;sDAmBhB,eAAe,GAAG,cAAc;mFAa9B,eAAe,aACP,MAAM,SACX,YAAY,GAAG,YAAY,GAAG,EAAE,GACrC,cAAc;kEAqBd,IAAI,GAAG,iBAAiB;CAW5B,CAAC;AAEF,eAAe,SAAS,CAAC"}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/templates.test.d.ts b/my_flask_app/static/choices.js/public/types/src/scripts/templates.test.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba2e643a953d7aff0a6dad8efc42c826dd78f145
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/templates.test.d.ts
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=templates.test.d.ts.map
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/public/types/src/scripts/templates.test.d.ts.map b/my_flask_app/static/choices.js/public/types/src/scripts/templates.test.d.ts.map
new file mode 100644
index 0000000000000000000000000000000000000000..d7d77ed38cfbac3795d0124939ac3e990fb3235d
--- /dev/null
+++ b/my_flask_app/static/choices.js/public/types/src/scripts/templates.test.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"templates.test.d.ts","sourceRoot":"","sources":["../../../../src/scripts/templates.test.ts"],"names":[],"mappings":""}
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/src/icons/cross-inverse.svg b/my_flask_app/static/choices.js/src/icons/cross-inverse.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9d3271cdbd872f8b012a42cabc2ccedb8f2ea2d2
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/icons/cross-inverse.svg
@@ -0,0 +1 @@
+<svg width="21" height="21" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="evenodd"><path d="M2.592.044l18.364 18.364-2.548 2.548L.044 2.592z"/><path d="M0 18.364L18.364 0l2.548 2.548L2.548 20.912z"/></g></svg>
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/src/icons/cross.svg b/my_flask_app/static/choices.js/src/icons/cross.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d8a60ebde71da2aa7ffd4084a3d9501050729b61
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/icons/cross.svg
@@ -0,0 +1 @@
+<svg width="21" height="21" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg"><g fill="#FFF" fill-rule="evenodd"><path d="M2.592.044l18.364 18.364-2.548 2.548L.044 2.592z"/><path d="M0 18.364L18.364 0l2.548 2.548L2.548 20.912z"/></g></svg>
\ No newline at end of file
diff --git a/my_flask_app/static/choices.js/src/index.ts b/my_flask_app/static/choices.js/src/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06972f16405ee71a18e076a23e1684e56598a840
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/index.ts
@@ -0,0 +1,8 @@
+import Choices from './scripts/choices';
+
+export * from './scripts/interfaces';
+export * from './scripts/constants';
+export * from './scripts/defaults';
+export { default as templates } from './scripts/templates';
+
+export default Choices;
diff --git a/my_flask_app/static/choices.js/src/scripts/actions/choices.test.ts b/my_flask_app/static/choices.js/src/scripts/actions/choices.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d023b2550b83dc01295550bacc8e505a59a2f433
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/actions/choices.test.ts
@@ -0,0 +1,92 @@
+import { expect } from 'chai';
+import * as actions from './choices';
+
+describe('actions/choices', () => {
+  describe('addChoice action', () => {
+    it('returns ADD_CHOICE action', () => {
+      const value = 'test';
+      const label = 'test';
+      const id = 1;
+      const groupId = 1;
+      const disabled = false;
+      const elementId = 1;
+      const customProperties = { test: true };
+      const placeholder = true;
+      const keyCode = 10;
+
+      const expectedAction: actions.AddChoiceAction = {
+        type: 'ADD_CHOICE',
+        value,
+        label,
+        id,
+        groupId,
+        disabled,
+        elementId,
+        customProperties,
+        placeholder,
+        keyCode,
+      };
+
+      expect(
+        actions.addChoice({
+          value,
+          label,
+          id,
+          groupId,
+          disabled,
+          elementId,
+          customProperties,
+          placeholder,
+          keyCode,
+        }),
+      ).to.eql(expectedAction);
+    });
+  });
+
+  describe('filterChoices action', () => {
+    it('returns FILTER_CHOICES action', () => {
+      const results = Array(10);
+      const expectedAction: actions.FilterChoicesAction = {
+        type: 'FILTER_CHOICES',
+        results,
+      };
+
+      expect(actions.filterChoices(results)).to.eql(expectedAction);
+    });
+  });
+
+  describe('activateChoices action', () => {
+    describe('not passing active parameter', () => {
+      it('returns ACTIVATE_CHOICES action', () => {
+        const expectedAction: actions.ActivateChoicesAction = {
+          type: 'ACTIVATE_CHOICES',
+          active: true,
+        };
+
+        expect(actions.activateChoices()).to.eql(expectedAction);
+      });
+    });
+
+    describe('passing active parameter', () => {
+      it('returns ACTIVATE_CHOICES action', () => {
+        const active = true;
+        const expectedAction: actions.ActivateChoicesAction = {
+          type: 'ACTIVATE_CHOICES',
+          active,
+        };
+
+        expect(actions.activateChoices(active)).to.eql(expectedAction);
+      });
+    });
+  });
+
+  describe('clearChoices action', () => {
+    it('returns CLEAR_CHOICES action', () => {
+      const expectedAction: actions.ClearChoicesAction = {
+        type: 'CLEAR_CHOICES',
+      };
+
+      expect(actions.clearChoices()).to.eql(expectedAction);
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/actions/choices.ts b/my_flask_app/static/choices.js/src/scripts/actions/choices.ts
new file mode 100644
index 0000000000000000000000000000000000000000..db95c99a54e127a8e1f02e2e21390523a77b5fd6
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/actions/choices.ts
@@ -0,0 +1,73 @@
+import { ACTION_TYPES } from '../constants';
+import { Choice } from '../interfaces/choice';
+
+export interface AddChoiceAction {
+  type: typeof ACTION_TYPES.ADD_CHOICE;
+  id: number;
+  value: string;
+  label: string;
+  groupId: number;
+  disabled: boolean;
+  elementId: number;
+  customProperties: object;
+  placeholder: boolean;
+  keyCode: number;
+}
+
+export interface Result<T> {
+  item: T;
+  score: number;
+}
+
+export interface FilterChoicesAction {
+  type: typeof ACTION_TYPES.FILTER_CHOICES;
+  results: Result<Choice>[];
+}
+
+export interface ActivateChoicesAction {
+  type: typeof ACTION_TYPES.ACTIVATE_CHOICES;
+  active: boolean;
+}
+
+export interface ClearChoicesAction {
+  type: typeof ACTION_TYPES.CLEAR_CHOICES;
+}
+
+export const addChoice = ({
+  value,
+  label,
+  id,
+  groupId,
+  disabled,
+  elementId,
+  customProperties,
+  placeholder,
+  keyCode,
+}): AddChoiceAction => ({
+  type: ACTION_TYPES.ADD_CHOICE,
+  value,
+  label,
+  id,
+  groupId,
+  disabled,
+  elementId,
+  customProperties,
+  placeholder,
+  keyCode,
+});
+
+export const filterChoices = (
+  results: Result<Choice>[],
+): FilterChoicesAction => ({
+  type: ACTION_TYPES.FILTER_CHOICES,
+  results,
+});
+
+export const activateChoices = (active = true): ActivateChoicesAction => ({
+  type: ACTION_TYPES.ACTIVATE_CHOICES,
+  active,
+});
+
+export const clearChoices = (): ClearChoicesAction => ({
+  type: ACTION_TYPES.CLEAR_CHOICES,
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/actions/groups.test.ts b/my_flask_app/static/choices.js/src/scripts/actions/groups.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f56e29c2727635175fb7a7b03f58da06fd60ded5
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/actions/groups.test.ts
@@ -0,0 +1,25 @@
+import { expect } from 'chai';
+import * as actions from './groups';
+
+describe('actions/groups', () => {
+  describe('addGroup action', () => {
+    it('returns ADD_GROUP action', () => {
+      const value = 'test';
+      const id = 1;
+      const active = true;
+      const disabled = false;
+
+      const expectedAction: actions.AddGroupAction = {
+        type: 'ADD_GROUP',
+        value,
+        id,
+        active,
+        disabled,
+      };
+
+      expect(actions.addGroup({ value, id, active, disabled })).to.eql(
+        expectedAction,
+      );
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/actions/groups.ts b/my_flask_app/static/choices.js/src/scripts/actions/groups.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c4a24e6e7f21055e274c0759262dc0dba14a7ba
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/actions/groups.ts
@@ -0,0 +1,27 @@
+import { ACTION_TYPES } from '../constants';
+
+export interface AddGroupAction {
+  type: typeof ACTION_TYPES.ADD_GROUP;
+  id: number;
+  value: string;
+  active: boolean;
+  disabled: boolean;
+}
+
+export const addGroup = ({
+  value,
+  id,
+  active,
+  disabled,
+}: {
+  id: number;
+  value: string;
+  active: boolean;
+  disabled: boolean;
+}): AddGroupAction => ({
+  type: ACTION_TYPES.ADD_GROUP,
+  value,
+  id,
+  active,
+  disabled,
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/actions/items.test.ts b/my_flask_app/static/choices.js/src/scripts/actions/items.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..28b9ba037511370273e6b98adaea26c5f0b7e65e
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/actions/items.test.ts
@@ -0,0 +1,72 @@
+import { expect } from 'chai';
+import * as actions from './items';
+
+describe('actions/items', () => {
+  describe('addItem action', () => {
+    it('returns ADD_ITEM action', () => {
+      const value = 'test';
+      const label = 'test';
+      const id = 1;
+      const choiceId = 1;
+      const groupId = 1;
+      const customProperties = { test: true };
+      const placeholder = true;
+      const keyCode = 10;
+
+      const expectedAction: actions.AddItemAction = {
+        type: 'ADD_ITEM',
+        value,
+        label,
+        id,
+        choiceId,
+        groupId,
+        customProperties,
+        placeholder,
+        keyCode,
+      };
+
+      expect(
+        actions.addItem({
+          value,
+          label,
+          id,
+          choiceId,
+          groupId,
+          customProperties,
+          placeholder,
+          keyCode,
+        }),
+      ).to.eql(expectedAction);
+    });
+  });
+
+  describe('removeItem action', () => {
+    it('returns REMOVE_ITEM action', () => {
+      const id = 1;
+      const choiceId = 1;
+
+      const expectedAction: actions.RemoveItemAction = {
+        type: 'REMOVE_ITEM',
+        id,
+        choiceId,
+      };
+
+      expect(actions.removeItem(id, choiceId)).to.eql(expectedAction);
+    });
+  });
+
+  describe('highlightItem action', () => {
+    it('returns HIGHLIGHT_ITEM action', () => {
+      const id = 1;
+      const highlighted = true;
+
+      const expectedAction: actions.HighlightItemAction = {
+        type: 'HIGHLIGHT_ITEM',
+        id,
+        highlighted,
+      };
+
+      expect(actions.highlightItem(id, highlighted)).to.eql(expectedAction);
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/actions/items.ts b/my_flask_app/static/choices.js/src/scripts/actions/items.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e7ec8a497fccb9ce4119f5b3740c15eae9bfbe18
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/actions/items.ts
@@ -0,0 +1,70 @@
+import { ACTION_TYPES } from '../constants';
+
+export interface AddItemAction {
+  type: typeof ACTION_TYPES.ADD_ITEM;
+  id: number;
+  value: string;
+  label: string;
+  choiceId: number;
+  groupId: number;
+  customProperties: object;
+  placeholder: boolean;
+  keyCode: number;
+}
+
+export interface RemoveItemAction {
+  type: typeof ACTION_TYPES.REMOVE_ITEM;
+  id: number;
+  choiceId: number;
+}
+
+export interface HighlightItemAction {
+  type: typeof ACTION_TYPES.HIGHLIGHT_ITEM;
+  id: number;
+  highlighted: boolean;
+}
+
+export const addItem = ({
+  value,
+  label,
+  id,
+  choiceId,
+  groupId,
+  customProperties,
+  placeholder,
+  keyCode,
+}: {
+  id: number;
+  value: string;
+  label: string;
+  choiceId: number;
+  groupId: number;
+  customProperties: object;
+  placeholder: boolean;
+  keyCode: number;
+}): AddItemAction => ({
+  type: ACTION_TYPES.ADD_ITEM,
+  value,
+  label,
+  id,
+  choiceId,
+  groupId,
+  customProperties,
+  placeholder,
+  keyCode,
+});
+
+export const removeItem = (id: number, choiceId: number): RemoveItemAction => ({
+  type: ACTION_TYPES.REMOVE_ITEM,
+  id,
+  choiceId,
+});
+
+export const highlightItem = (
+  id: number,
+  highlighted: boolean,
+): HighlightItemAction => ({
+  type: ACTION_TYPES.HIGHLIGHT_ITEM,
+  id,
+  highlighted,
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/actions/misc.test.ts b/my_flask_app/static/choices.js/src/scripts/actions/misc.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b9b0d9f23913102d93fe3aa62db698cf1205e0e8
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/actions/misc.test.ts
@@ -0,0 +1,56 @@
+import { expect } from 'chai';
+import { State } from '../interfaces/state';
+import * as actions from './misc';
+
+describe('actions/misc', () => {
+  describe('clearAll action', () => {
+    it('returns CLEAR_ALL action', () => {
+      const expectedAction: actions.ClearAllAction = {
+        type: 'CLEAR_ALL',
+      };
+
+      expect(actions.clearAll()).to.eql(expectedAction);
+    });
+  });
+
+  describe('resetTo action', () => {
+    it('returns RESET_TO action', () => {
+      const state: State = {
+        choices: [],
+        items: [],
+        groups: [],
+        loading: false,
+      };
+      const expectedAction: actions.ResetToAction = {
+        type: 'RESET_TO',
+        state,
+      };
+
+      expect(actions.resetTo(state)).to.eql(expectedAction);
+    });
+  });
+
+  describe('setIsLoading action', () => {
+    describe('setting loading state to true', () => {
+      it('returns expected action', () => {
+        const expectedAction: actions.SetIsLoadingAction = {
+          type: 'SET_IS_LOADING',
+          isLoading: true,
+        };
+
+        expect(actions.setIsLoading(true)).to.eql(expectedAction);
+      });
+    });
+
+    describe('setting loading state to false', () => {
+      it('returns expected action', () => {
+        const expectedAction: actions.SetIsLoadingAction = {
+          type: 'SET_IS_LOADING',
+          isLoading: false,
+        };
+
+        expect(actions.setIsLoading(false)).to.eql(expectedAction);
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/actions/misc.ts b/my_flask_app/static/choices.js/src/scripts/actions/misc.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c667b77bddb6859075bbacd27e68980b0ccf9a8e
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/actions/misc.ts
@@ -0,0 +1,30 @@
+import { ACTION_TYPES } from '../constants';
+import { State } from '../interfaces/state';
+
+export interface ClearAllAction {
+  type: typeof ACTION_TYPES.CLEAR_ALL;
+}
+
+export interface ResetToAction {
+  type: typeof ACTION_TYPES.RESET_TO;
+  state: State;
+}
+
+export interface SetIsLoadingAction {
+  type: typeof ACTION_TYPES.SET_IS_LOADING;
+  isLoading: boolean;
+}
+
+export const clearAll = (): ClearAllAction => ({
+  type: ACTION_TYPES.CLEAR_ALL,
+});
+
+export const resetTo = (state: State): ResetToAction => ({
+  type: ACTION_TYPES.RESET_TO,
+  state,
+});
+
+export const setIsLoading = (isLoading: boolean): SetIsLoadingAction => ({
+  type: ACTION_TYPES.SET_IS_LOADING,
+  isLoading,
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/choices.test.ts b/my_flask_app/static/choices.js/src/scripts/choices.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..20629fbe5afa903bee0c093cac4391c0ae93a5e4
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/choices.test.ts
@@ -0,0 +1,2361 @@
+import chai, { expect } from 'chai';
+import { spy, stub } from 'sinon';
+import sinonChai from 'sinon-chai';
+
+import Choices from './choices';
+
+import { EVENTS, ACTION_TYPES, KEY_CODES } from './constants';
+import { WrappedSelect, WrappedInput } from './components/index';
+import { removeItem } from './actions/items';
+import templates from './templates';
+import { Choice } from './interfaces/choice';
+import { Group } from './interfaces/group';
+import { Item } from './interfaces/item';
+import { DEFAULT_CONFIG } from './defaults';
+
+chai.use(sinonChai);
+
+describe('choices', () => {
+  let instance;
+  let output;
+  let passedElement;
+
+  beforeEach(() => {
+    passedElement = document.createElement('input');
+    passedElement.type = 'text';
+    passedElement.className = 'js-choices';
+    document.body.appendChild(passedElement);
+
+    instance = new Choices(passedElement, { allowHTML: true });
+  });
+
+  afterEach(() => {
+    output = null;
+    instance = null;
+  });
+
+  describe('constructor', () => {
+    describe('config', () => {
+      describe('not passing config options', () => {
+        it('uses the default config', () => {
+          document.body.innerHTML = `
+          <input data-choice type="text" id="input-1" />
+          `;
+
+          instance = new Choices();
+
+          expect(instance.config).to.eql(DEFAULT_CONFIG);
+        });
+      });
+
+      describe('passing config options', () => {
+        it('merges the passed config with the default config', () => {
+          document.body.innerHTML = `
+          <input data-choice type="text" id="input-1" />
+          `;
+
+          const config = {
+            allowHTML: true,
+            renderChoiceLimit: 5,
+          };
+          instance = new Choices('[data-choice]', config);
+
+          expect(instance.config).to.eql({
+            ...DEFAULT_CONFIG,
+            ...config,
+          });
+        });
+
+        describe('passing the searchEnabled config option with a value of false', () => {
+          describe('passing a select-multiple element', () => {
+            it('sets searchEnabled to true', () => {
+              document.body.innerHTML = `
+              <select data-choice multiple></select>
+              `;
+
+              instance = new Choices('[data-choice]', {
+                allowHTML: true,
+                searchEnabled: false,
+              });
+
+              expect(instance.config.searchEnabled).to.equal(true);
+            });
+          });
+        });
+
+        describe('passing the renderSelectedChoices config option with an unexpected value', () => {
+          it('sets renderSelectedChoices to "auto"', () => {
+            document.body.innerHTML = `
+            <select data-choice multiple></select>
+            `;
+
+            instance = new Choices('[data-choice]', {
+              allowHTML: true,
+              renderSelectedChoices: 'test' as any,
+            });
+
+            expect(instance.config.renderSelectedChoices).to.equal('auto');
+          });
+        });
+      });
+    });
+
+    describe('not passing an element', () => {
+      it('returns a Choices instance for the first element with a "data-choice" attribute', () => {
+        document.body.innerHTML = `
+        <input data-choice type="text" id="input-1" />
+        <input data-choice type="text" id="input-2" />
+        <input data-choice type="text" id="input-3" />
+        `;
+
+        const inputs = document.querySelectorAll('[data-choice]');
+        expect(inputs.length).to.equal(3);
+
+        instance = new Choices(undefined, { allowHTML: true });
+
+        expect(instance.passedElement.element.id).to.equal(inputs[0].id);
+      });
+
+      describe('when an element cannot be found in the DOM', () => {
+        it('throws an error', () => {
+          document.body.innerHTML = ``;
+          expect(() => new Choices(undefined, { allowHTML: true })).to.throw(
+            TypeError,
+            'Expected one of the following types text|select-one|select-multiple',
+          );
+        });
+      });
+    });
+
+    describe('passing an element', () => {
+      describe('passing an element that has not been initialised with Choices', () => {
+        beforeEach(() => {
+          document.body.innerHTML = `
+          <input type="text" id="input-1" />
+          `;
+        });
+
+        it('sets the initialised flag to true', () => {
+          instance = new Choices('#input-1', { allowHTML: true });
+          expect(instance.initialised).to.equal(true);
+        });
+
+        it('intialises', () => {
+          const initSpy = spy();
+          // initialise with the same element
+          instance = new Choices('#input-1', {
+            allowHTML: true,
+            silent: true,
+            callbackOnInit: initSpy,
+          });
+
+          expect(initSpy.called).to.equal(true);
+        });
+      });
+
+      describe('passing an element that has already be initialised with Choices', () => {
+        beforeEach(() => {
+          document.body.innerHTML = `
+          <input type="text" id="input-1" />
+          `;
+
+          // initialise once
+          new Choices('#input-1', { allowHTML: true, silent: true });
+        });
+
+        it('sets the initialised flag to true', () => {
+          // initialise with the same element
+          instance = new Choices('#input-1', { allowHTML: true, silent: true });
+
+          expect(instance.initialised).to.equal(true);
+        });
+
+        it('does not reinitialise', () => {
+          const initSpy = spy();
+          // initialise with the same element
+          instance = new Choices('#input-1', {
+            allowHTML: true,
+            silent: true,
+            callbackOnInit: initSpy,
+          });
+
+          expect(initSpy.called).to.equal(false);
+        });
+      });
+
+      describe(`passing an element as a DOMString`, () => {
+        describe('passing a input element type', () => {
+          it('sets the "passedElement" instance property as an instance of WrappedInput', () => {
+            document.body.innerHTML = `
+            <input data-choice type="text" id="input-1" />
+            `;
+
+            instance = new Choices('[data-choice]', { allowHTML: true });
+
+            expect(instance.passedElement).to.be.an.instanceOf(WrappedInput);
+          });
+        });
+
+        describe('passing a select element type', () => {
+          it('sets the "passedElement" instance property as an instance of WrappedSelect', () => {
+            document.body.innerHTML = `
+            <select data-choice id="select-1"></select>
+            `;
+
+            instance = new Choices('[data-choice]', { allowHTML: true });
+
+            expect(instance.passedElement).to.be.an.instanceOf(WrappedSelect);
+          });
+        });
+      });
+
+      describe(`passing an element as a HTMLElement`, () => {
+        describe('passing a input element type', () => {
+          it('sets the "passedElement" instance property as an instance of WrappedInput', () => {
+            document.body.innerHTML = `
+            <input data-choice type="text" id="input-1" />
+            `;
+
+            instance = new Choices('[data-choice]', { allowHTML: true });
+
+            expect(instance.passedElement).to.be.an.instanceOf(WrappedInput);
+          });
+        });
+
+        describe('passing a select element type', () => {
+          it('sets the "passedElement" instance property as an instance of WrappedSelect', () => {
+            document.body.innerHTML = `
+            <select data-choice id="select-1"></select>
+            `;
+
+            instance = new Choices('[data-choice]', { allowHTML: true });
+
+            expect(instance.passedElement).to.be.an.instanceOf(WrappedSelect);
+          });
+        });
+      });
+
+      describe('passing an invalid element type', () => {
+        it('throws an TypeError', () => {
+          document.body.innerHTML = `
+          <div data-choice id="div-1"></div>
+          `;
+          expect(
+            () => new Choices('[data-choice]', { allowHTML: true }),
+          ).to.throw(
+            TypeError,
+            'Expected one of the following types text|select-one|select-multiple',
+          );
+        });
+      });
+    });
+  });
+
+  describe('public methods', () => {
+    describe('init', () => {
+      const callbackOnInitSpy = spy();
+
+      beforeEach(() => {
+        instance = new Choices(passedElement, {
+          allowHTML: true,
+          callbackOnInit: callbackOnInitSpy,
+          silent: true,
+        });
+      });
+
+      describe('when already initialised', () => {
+        beforeEach(() => {
+          instance.initialised = true;
+          instance.init();
+        });
+
+        it("doesn't set initialise flag", () => {
+          expect(instance.initialised).to.not.equal(false);
+        });
+      });
+
+      describe('not already initialised', () => {
+        let createTemplatesSpy;
+        let createInputSpy;
+        let storeSubscribeSpy;
+        let renderSpy;
+        let addEventListenersSpy;
+
+        beforeEach(() => {
+          createTemplatesSpy = spy(instance, '_createTemplates');
+          createInputSpy = spy(instance, '_createStructure');
+          storeSubscribeSpy = spy(instance._store, 'subscribe');
+          renderSpy = spy(instance, '_render');
+          addEventListenersSpy = spy(instance, '_addEventListeners');
+
+          instance.initialised = false;
+          instance.init();
+        });
+
+        afterEach(() => {
+          createTemplatesSpy.restore();
+          createInputSpy.restore();
+          storeSubscribeSpy.restore();
+          renderSpy.restore();
+          addEventListenersSpy.restore();
+        });
+
+        it('sets initialise flag', () => {
+          expect(instance.initialised).to.equal(true);
+        });
+
+        it('creates templates', () => {
+          expect(createTemplatesSpy.called).to.equal(true);
+        });
+
+        it('creates input', () => {
+          expect(createInputSpy.called).to.equal(true);
+        });
+
+        it('subscribes to store with render method', () => {
+          expect(storeSubscribeSpy.called).to.equal(true);
+          expect(storeSubscribeSpy.lastCall.args[0]).to.equal(instance._render);
+        });
+
+        it('fires initial render', () => {
+          expect(renderSpy.called).to.equal(true);
+        });
+
+        it('adds event listeners', () => {
+          expect(addEventListenersSpy.called).to.equal(true);
+        });
+
+        it('fires callback', () => {
+          expect(callbackOnInitSpy.called).to.equal(true);
+        });
+      });
+    });
+
+    describe('destroy', () => {
+      beforeEach(() => {
+        passedElement = document.createElement('input');
+        passedElement.type = 'text';
+        passedElement.className = 'js-choices';
+        document.body.appendChild(passedElement);
+
+        instance = new Choices(passedElement, { allowHTML: true });
+      });
+
+      describe('not already initialised', () => {
+        beforeEach(() => {
+          instance.initialised = false;
+          instance.destroy();
+        });
+
+        it("doesn't set initialise flag", () => {
+          expect(instance.initialised).to.not.equal(true);
+        });
+      });
+
+      describe('when already initialised', () => {
+        let removeEventListenersSpy;
+        let passedElementRevealSpy;
+        let containerOuterUnwrapSpy;
+        let clearStoreSpy;
+
+        beforeEach(() => {
+          removeEventListenersSpy = spy(instance, '_removeEventListeners');
+          passedElementRevealSpy = spy(instance.passedElement, 'reveal');
+          containerOuterUnwrapSpy = spy(instance.containerOuter, 'unwrap');
+          clearStoreSpy = spy(instance, 'clearStore');
+
+          instance.initialised = true;
+          instance.destroy();
+        });
+
+        afterEach(() => {
+          removeEventListenersSpy.restore();
+          passedElementRevealSpy.restore();
+          containerOuterUnwrapSpy.restore();
+          clearStoreSpy.restore();
+        });
+
+        it('removes event listeners', () => {
+          expect(removeEventListenersSpy.called).to.equal(true);
+        });
+
+        it('reveals passed element', () => {
+          expect(passedElementRevealSpy.called).to.equal(true);
+        });
+
+        it('reverts outer container', () => {
+          expect(containerOuterUnwrapSpy.called).to.equal(true);
+          expect(containerOuterUnwrapSpy.lastCall.args[0]).to.equal(
+            instance.passedElement.element,
+          );
+        });
+
+        it('clears store', () => {
+          expect(clearStoreSpy.called).to.equal(true);
+        });
+
+        it('restes templates config', () => {
+          expect(instance._templates).to.deep.equal(templates);
+        });
+
+        it('resets initialise flag', () => {
+          expect(instance.initialised).to.equal(false);
+        });
+      });
+    });
+
+    describe('enable', () => {
+      let passedElementEnableSpy;
+      let addEventListenersSpy;
+      let containerOuterEnableSpy;
+      let inputEnableSpy;
+
+      beforeEach(() => {
+        addEventListenersSpy = spy(instance, '_addEventListeners');
+        passedElementEnableSpy = spy(instance.passedElement, 'enable');
+        containerOuterEnableSpy = spy(instance.containerOuter, 'enable');
+        inputEnableSpy = spy(instance.input, 'enable');
+      });
+
+      afterEach(() => {
+        addEventListenersSpy.restore();
+        passedElementEnableSpy.restore();
+        containerOuterEnableSpy.restore();
+        inputEnableSpy.restore();
+      });
+
+      describe('when already enabled', () => {
+        beforeEach(() => {
+          instance.passedElement.isDisabled = false;
+          instance.containerOuter.isDisabled = false;
+          output = instance.enable();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('returns early', () => {
+          expect(passedElementEnableSpy.called).to.equal(false);
+          expect(addEventListenersSpy.called).to.equal(false);
+          expect(inputEnableSpy.called).to.equal(false);
+          expect(containerOuterEnableSpy.called).to.equal(false);
+        });
+      });
+
+      describe('when not already enabled', () => {
+        beforeEach(() => {
+          instance.passedElement.isDisabled = true;
+          instance.containerOuter.isDisabled = true;
+          instance.enable();
+        });
+
+        it('adds event listeners', () => {
+          expect(addEventListenersSpy.called).to.equal(true);
+        });
+
+        it('enables input', () => {
+          expect(inputEnableSpy.called).to.equal(true);
+        });
+
+        it('enables containerOuter', () => {
+          expect(containerOuterEnableSpy.called).to.equal(true);
+        });
+      });
+    });
+
+    describe('disable', () => {
+      let removeEventListenersSpy;
+      let passedElementDisableSpy;
+      let containerOuterDisableSpy;
+      let inputDisableSpy;
+
+      beforeEach(() => {
+        removeEventListenersSpy = spy(instance, '_removeEventListeners');
+        passedElementDisableSpy = spy(instance.passedElement, 'disable');
+        containerOuterDisableSpy = spy(instance.containerOuter, 'disable');
+        inputDisableSpy = spy(instance.input, 'disable');
+      });
+
+      afterEach(() => {
+        removeEventListenersSpy.restore();
+        passedElementDisableSpy.restore();
+        containerOuterDisableSpy.restore();
+        inputDisableSpy.restore();
+      });
+
+      describe('when already disabled', () => {
+        beforeEach(() => {
+          instance.passedElement.isDisabled = true;
+          instance.containerOuter.isDisabled = true;
+          output = instance.disable();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('returns early', () => {
+          expect(removeEventListenersSpy.called).to.equal(false);
+          expect(passedElementDisableSpy.called).to.equal(false);
+          expect(containerOuterDisableSpy.called).to.equal(false);
+          expect(inputDisableSpy.called).to.equal(false);
+        });
+      });
+
+      describe('when not already disabled', () => {
+        beforeEach(() => {
+          instance.passedElement.isDisabled = false;
+          instance.containerOuter.isDisabled = false;
+          output = instance.disable();
+        });
+
+        it('removes event listeners', () => {
+          expect(removeEventListenersSpy.called).to.equal(true);
+        });
+
+        it('disables input', () => {
+          expect(inputDisableSpy.called).to.equal(true);
+        });
+
+        it('enables containerOuter', () => {
+          expect(containerOuterDisableSpy.called).to.equal(true);
+        });
+      });
+    });
+
+    describe('showDropdown', () => {
+      let containerOuterOpenSpy;
+      let dropdownShowSpy;
+      let inputFocusSpy;
+      let passedElementTriggerEventStub;
+
+      beforeEach(() => {
+        containerOuterOpenSpy = spy(instance.containerOuter, 'open');
+        dropdownShowSpy = spy(instance.dropdown, 'show');
+        inputFocusSpy = spy(instance.input, 'focus');
+        passedElementTriggerEventStub = stub();
+
+        instance.passedElement.triggerEvent = passedElementTriggerEventStub;
+      });
+
+      afterEach(() => {
+        containerOuterOpenSpy.restore();
+        dropdownShowSpy.restore();
+        inputFocusSpy.restore();
+        instance.passedElement.triggerEvent.reset();
+      });
+
+      describe('dropdown active', () => {
+        beforeEach(() => {
+          instance.dropdown.isActive = true;
+          output = instance.showDropdown();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('returns early', () => {
+          expect(containerOuterOpenSpy.called).to.equal(false);
+          expect(dropdownShowSpy.called).to.equal(false);
+          expect(inputFocusSpy.called).to.equal(false);
+          expect(passedElementTriggerEventStub.called).to.equal(false);
+        });
+      });
+
+      describe('dropdown inactive', () => {
+        beforeEach(() => {
+          instance.dropdown.isActive = false;
+          output = instance.showDropdown();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('opens containerOuter', (done) => {
+          requestAnimationFrame(() => {
+            expect(containerOuterOpenSpy.called).to.equal(true);
+            done();
+          });
+        });
+
+        it('shows dropdown with blurInput flag', (done) => {
+          requestAnimationFrame(() => {
+            expect(dropdownShowSpy.called).to.equal(true);
+            done();
+          });
+        });
+
+        it('triggers event on passedElement', (done) => {
+          requestAnimationFrame(() => {
+            expect(passedElementTriggerEventStub.called).to.equal(true);
+            expect(passedElementTriggerEventStub.lastCall.args[0]).to.eql(
+              EVENTS.showDropdown,
+            );
+            expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({});
+            done();
+          });
+        });
+
+        describe('passing true focusInput flag with canSearch set to true', () => {
+          beforeEach(() => {
+            instance.dropdown.isActive = false;
+            instance._canSearch = true;
+            output = instance.showDropdown(true);
+          });
+
+          it('focuses input', (done) => {
+            requestAnimationFrame(() => {
+              expect(inputFocusSpy.called).to.equal(true);
+              done();
+            });
+          });
+        });
+      });
+    });
+
+    describe('hideDropdown', () => {
+      let containerOuterCloseSpy;
+      let dropdownHideSpy;
+      let inputBlurSpy;
+      let inputRemoveActiveDescendantSpy;
+      let passedElementTriggerEventStub;
+
+      beforeEach(() => {
+        containerOuterCloseSpy = spy(instance.containerOuter, 'close');
+        dropdownHideSpy = spy(instance.dropdown, 'hide');
+        inputBlurSpy = spy(instance.input, 'blur');
+        inputRemoveActiveDescendantSpy = spy(
+          instance.input,
+          'removeActiveDescendant',
+        );
+        passedElementTriggerEventStub = stub();
+
+        instance.passedElement.triggerEvent = passedElementTriggerEventStub;
+      });
+
+      afterEach(() => {
+        containerOuterCloseSpy.restore();
+        dropdownHideSpy.restore();
+        inputBlurSpy.restore();
+        inputRemoveActiveDescendantSpy.restore();
+        instance.passedElement.triggerEvent.reset();
+      });
+
+      describe('dropdown inactive', () => {
+        beforeEach(() => {
+          instance.dropdown.isActive = false;
+          output = instance.hideDropdown();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('returns early', () => {
+          expect(containerOuterCloseSpy.called).to.equal(false);
+          expect(dropdownHideSpy.called).to.equal(false);
+          expect(inputBlurSpy.called).to.equal(false);
+          expect(passedElementTriggerEventStub.called).to.equal(false);
+        });
+      });
+
+      describe('dropdown active', () => {
+        beforeEach(() => {
+          instance.dropdown.isActive = true;
+          output = instance.hideDropdown();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('closes containerOuter', (done) => {
+          requestAnimationFrame(() => {
+            expect(containerOuterCloseSpy.called).to.equal(true);
+            done();
+          });
+        });
+
+        it('hides dropdown with blurInput flag', (done) => {
+          requestAnimationFrame(() => {
+            expect(dropdownHideSpy.called).to.equal(true);
+            done();
+          });
+        });
+
+        it('triggers event on passedElement', (done) => {
+          requestAnimationFrame(() => {
+            expect(passedElementTriggerEventStub.called).to.equal(true);
+            expect(passedElementTriggerEventStub.lastCall.args[0]).to.eql(
+              EVENTS.hideDropdown,
+            );
+            expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({});
+            done();
+          });
+        });
+
+        describe('passing true blurInput flag with canSearch set to true', () => {
+          beforeEach(() => {
+            instance.dropdown.isActive = true;
+            instance._canSearch = true;
+            output = instance.hideDropdown(true);
+          });
+
+          it('removes active descendants', (done) => {
+            requestAnimationFrame(() => {
+              expect(inputRemoveActiveDescendantSpy.called).to.equal(true);
+              done();
+            });
+          });
+
+          it('blurs input', (done) => {
+            requestAnimationFrame(() => {
+              expect(inputBlurSpy.called).to.equal(true);
+              done();
+            });
+          });
+        });
+      });
+    });
+
+    describe('highlightItem', () => {
+      let passedElementTriggerEventStub;
+      let storeDispatchSpy;
+      let storeGetGroupByIdStub;
+      const groupIdValue = 'Test';
+
+      beforeEach(() => {
+        passedElementTriggerEventStub = stub();
+        storeGetGroupByIdStub = stub().returns({
+          value: groupIdValue,
+        });
+        storeDispatchSpy = spy(instance._store, 'dispatch');
+
+        instance._store.getGroupById = storeGetGroupByIdStub;
+        instance.passedElement.triggerEvent = passedElementTriggerEventStub;
+      });
+
+      afterEach(() => {
+        storeDispatchSpy.restore();
+        instance._store.getGroupById.reset();
+        instance.passedElement.triggerEvent.reset();
+      });
+
+      describe('no item passed', () => {
+        beforeEach(() => {
+          output = instance.highlightItem();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('returns early', () => {
+          expect(passedElementTriggerEventStub.called).to.equal(false);
+          expect(storeDispatchSpy.called).to.equal(false);
+          expect(storeGetGroupByIdStub.called).to.equal(false);
+        });
+      });
+
+      describe('item passed', () => {
+        const item: Item = {
+          id: 1234,
+          value: 'Test',
+          label: 'Test',
+        };
+
+        describe('passing truthy second paremeter', () => {
+          beforeEach(() => {
+            output = instance.highlightItem(item, true);
+          });
+
+          it('returns this', () => {
+            expect(output).to.eql(instance);
+          });
+
+          it('dispatches highlightItem action with correct arguments', () => {
+            expect(storeDispatchSpy.called).to.equal(true);
+            expect(storeDispatchSpy.lastCall.args[0]).to.eql({
+              type: ACTION_TYPES.HIGHLIGHT_ITEM,
+              id: item.id,
+              highlighted: true,
+            });
+          });
+
+          describe('item with negative groupId', () => {
+            beforeEach(() => {
+              item.groupId = -1;
+              output = instance.highlightItem(item);
+            });
+
+            it('triggers event with null groupValue', () => {
+              expect(passedElementTriggerEventStub.called).to.equal(true);
+              expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(
+                EVENTS.highlightItem,
+              );
+              expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({
+                id: item.id,
+                value: item.value,
+                label: item.label,
+                groupValue: null,
+              });
+            });
+          });
+
+          describe('item without groupId', () => {
+            beforeEach(() => {
+              item.groupId = 1;
+              output = instance.highlightItem(item);
+            });
+
+            it('triggers event with groupValue', () => {
+              expect(passedElementTriggerEventStub.called).to.equal(true);
+              expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(
+                EVENTS.highlightItem,
+              );
+              expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({
+                id: item.id,
+                value: item.value,
+                label: item.label,
+                groupValue: groupIdValue,
+              });
+            });
+          });
+        });
+
+        describe('passing falsey second paremeter', () => {
+          beforeEach(() => {
+            output = instance.highlightItem(item, false);
+          });
+
+          it("doesn't trigger event", () => {
+            expect(passedElementTriggerEventStub.called).to.equal(false);
+          });
+
+          it('returns this', () => {
+            expect(output).to.eql(instance);
+          });
+        });
+      });
+    });
+
+    describe('unhighlightItem', () => {
+      let passedElementTriggerEventStub;
+      let storeDispatchSpy;
+      let storeGetGroupByIdStub;
+      const groupIdValue = 'Test';
+
+      beforeEach(() => {
+        passedElementTriggerEventStub = stub();
+        storeGetGroupByIdStub = stub().returns({
+          value: groupIdValue,
+        });
+        storeDispatchSpy = spy(instance._store, 'dispatch');
+
+        instance._store.getGroupById = storeGetGroupByIdStub;
+        instance.passedElement.triggerEvent = passedElementTriggerEventStub;
+      });
+
+      afterEach(() => {
+        storeDispatchSpy.restore();
+        instance._store.getGroupById.reset();
+        instance.passedElement.triggerEvent.reset();
+      });
+
+      describe('no item passed', () => {
+        beforeEach(() => {
+          output = instance.unhighlightItem();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('returns early', () => {
+          expect(passedElementTriggerEventStub.called).to.equal(false);
+          expect(storeDispatchSpy.called).to.equal(false);
+          expect(storeGetGroupByIdStub.called).to.equal(false);
+        });
+      });
+
+      describe('item passed', () => {
+        const item: Item = {
+          id: 1234,
+          value: 'Test',
+          label: 'Test',
+        };
+
+        describe('passing truthy second paremeter', () => {
+          beforeEach(() => {
+            output = instance.unhighlightItem(item, true);
+          });
+
+          it('returns this', () => {
+            expect(output).to.eql(instance);
+          });
+
+          it('dispatches highlightItem action with correct arguments', () => {
+            expect(storeDispatchSpy.called).to.equal(true);
+            expect(storeDispatchSpy.lastCall.args[0]).to.eql({
+              type: ACTION_TYPES.HIGHLIGHT_ITEM,
+              id: item.id,
+              highlighted: false,
+            });
+          });
+
+          describe('item with negative groupId', () => {
+            beforeEach(() => {
+              item.groupId = -1;
+              output = instance.unhighlightItem(item);
+            });
+
+            it('triggers event with null groupValue', () => {
+              expect(passedElementTriggerEventStub.called).to.equal(true);
+              expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(
+                EVENTS.highlightItem,
+              );
+              expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({
+                id: item.id,
+                value: item.value,
+                label: item.label,
+                groupValue: null,
+              });
+            });
+          });
+
+          describe('item without groupId', () => {
+            beforeEach(() => {
+              item.groupId = 1;
+              output = instance.highlightItem(item);
+            });
+
+            it('triggers event with groupValue', () => {
+              expect(passedElementTriggerEventStub.called).to.equal(true);
+              expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(
+                EVENTS.highlightItem,
+              );
+              expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({
+                id: item.id,
+                value: item.value,
+                label: item.label,
+                groupValue: groupIdValue,
+              });
+            });
+          });
+        });
+
+        describe('passing falsey second paremeter', () => {
+          beforeEach(() => {
+            output = instance.highlightItem(item, false);
+          });
+
+          it("doesn't trigger event", () => {
+            expect(passedElementTriggerEventStub.called).to.equal(false);
+          });
+
+          it('returns this', () => {
+            expect(output).to.eql(instance);
+          });
+        });
+      });
+    });
+
+    describe('highlightAll', () => {
+      let storeGetItemsStub;
+      let highlightItemStub;
+
+      const items = [
+        {
+          id: 1,
+          value: 'Test 1',
+        },
+        {
+          id: 2,
+          value: 'Test 2',
+        },
+      ];
+
+      beforeEach(() => {
+        storeGetItemsStub = stub(instance._store, 'items').get(() => items);
+        highlightItemStub = stub();
+
+        instance.highlightItem = highlightItemStub;
+
+        output = instance.highlightAll();
+      });
+
+      afterEach(() => {
+        highlightItemStub.reset();
+        storeGetItemsStub.reset();
+      });
+
+      it('returns this', () => {
+        expect(output).to.eql(instance);
+      });
+
+      it('highlights each item in store', () => {
+        expect(highlightItemStub.callCount).to.equal(items.length);
+        expect(highlightItemStub.firstCall.args[0]).to.equal(items[0]);
+        expect(highlightItemStub.lastCall.args[0]).to.equal(items[1]);
+      });
+    });
+
+    describe('unhighlightAll', () => {
+      let storeGetItemsStub;
+      let unhighlightItemStub;
+
+      const items = [
+        {
+          id: 1,
+          value: 'Test 1',
+        },
+        {
+          id: 2,
+          value: 'Test 2',
+        },
+      ];
+
+      beforeEach(() => {
+        storeGetItemsStub = stub(instance._store, 'items').get(() => items);
+        unhighlightItemStub = stub();
+
+        instance.unhighlightItem = unhighlightItemStub;
+
+        output = instance.unhighlightAll();
+      });
+
+      afterEach(() => {
+        instance.unhighlightItem.reset();
+        storeGetItemsStub.reset();
+      });
+
+      it('returns this', () => {
+        expect(output).to.eql(instance);
+      });
+
+      it('unhighlights each item in store', () => {
+        expect(unhighlightItemStub.callCount).to.equal(items.length);
+        expect(unhighlightItemStub.firstCall.args[0]).to.equal(items[0]);
+        expect(unhighlightItemStub.lastCall.args[0]).to.equal(items[1]);
+      });
+    });
+
+    describe('clearChoices', () => {
+      let storeDispatchStub;
+
+      beforeEach(() => {
+        storeDispatchStub = stub();
+        instance._store.dispatch = storeDispatchStub;
+
+        output = instance.clearChoices();
+      });
+
+      afterEach(() => {
+        instance._store.dispatch.reset();
+      });
+
+      it('returns this', () => {
+        expect(output).to.eql(instance);
+      });
+
+      it('dispatches clearChoices action', () => {
+        expect(storeDispatchStub.lastCall.args[0]).to.eql({
+          type: ACTION_TYPES.CLEAR_CHOICES,
+        });
+      });
+    });
+
+    describe('clearStore', () => {
+      let storeDispatchStub;
+
+      beforeEach(() => {
+        storeDispatchStub = stub();
+        instance._store.dispatch = storeDispatchStub;
+
+        output = instance.clearStore();
+      });
+
+      afterEach(() => {
+        instance._store.dispatch.reset();
+      });
+
+      it('returns this', () => {
+        expect(output).to.eql(instance);
+      });
+
+      it('dispatches clearAll action', () => {
+        expect(storeDispatchStub.lastCall.args[0]).to.eql({
+          type: ACTION_TYPES.CLEAR_ALL,
+        });
+      });
+    });
+
+    describe('clearInput', () => {
+      let inputClearSpy;
+      let storeDispatchStub;
+
+      beforeEach(() => {
+        inputClearSpy = spy(instance.input, 'clear');
+        storeDispatchStub = stub();
+        instance._store.dispatch = storeDispatchStub;
+        output = instance.clearInput();
+      });
+
+      afterEach(() => {
+        inputClearSpy.restore();
+        instance._store.dispatch.reset();
+      });
+
+      it('returns this', () => {
+        expect(output).to.eql(instance);
+      });
+
+      describe('text element', () => {
+        beforeEach(() => {
+          instance._isSelectOneElement = false;
+          instance._isTextElement = false;
+
+          output = instance.clearInput();
+        });
+
+        it('clears input with correct arguments', () => {
+          expect(inputClearSpy.called).to.equal(true);
+          expect(inputClearSpy.lastCall.args[0]).to.equal(true);
+        });
+      });
+
+      describe('select element with search enabled', () => {
+        beforeEach(() => {
+          instance._isSelectOneElement = true;
+          instance._isTextElement = false;
+          instance.config.searchEnabled = true;
+
+          output = instance.clearInput();
+        });
+
+        it('clears input with correct arguments', () => {
+          expect(inputClearSpy.called).to.equal(true);
+          expect(inputClearSpy.lastCall.args[0]).to.equal(false);
+        });
+
+        it('resets search flag', () => {
+          expect(instance._isSearching).to.equal(false);
+        });
+
+        it('dispatches activateChoices action', () => {
+          expect(storeDispatchStub.called).to.equal(true);
+          expect(storeDispatchStub.lastCall.args[0]).to.eql({
+            type: ACTION_TYPES.ACTIVATE_CHOICES,
+            active: true,
+          });
+        });
+      });
+    });
+
+    describe('setChoices with callback/Promise', () => {
+      describe('not initialised', () => {
+        beforeEach(() => {
+          instance.initialised = false;
+        });
+
+        it('should throw', () => {
+          expect(() => instance.setChoices(null)).Throw(ReferenceError);
+        });
+      });
+
+      describe('text element', () => {
+        beforeEach(() => {
+          instance._isSelectElement = false;
+        });
+
+        it('should throw', () => {
+          expect(() => instance.setChoices(null)).Throw(TypeError);
+        });
+      });
+
+      describe('passing invalid function', () => {
+        beforeEach(() => {
+          instance._isSelectElement = true;
+        });
+
+        it('should throw on non function', () => {
+          expect(() => instance.setChoices(null)).Throw(TypeError, /Promise/i);
+        });
+
+        it(`should throw on function that doesn't return promise`, () => {
+          expect(() => instance.setChoices(() => 'boo')).to.throw(
+            TypeError,
+            /promise/i,
+          );
+        });
+      });
+
+      describe('select element', () => {
+        it('fetches and sets choices', async () => {
+          document.body.innerHTML = '<select id="test" />';
+          const choice = new Choices('#test', { allowHTML: true });
+          const handleLoadingStateSpy = spy(choice, '_handleLoadingState');
+
+          let fetcherCalled = false;
+          const fetcher = async (inst): Promise<Choice[]> => {
+            expect(inst).to.eq(choice);
+            fetcherCalled = true;
+            // eslint-disable-next-line no-promise-executor-return
+            await new Promise((resolve) => setTimeout(resolve, 800));
+
+            return [
+              { label: 'l1', value: 'v1', customProperties: { prop1: true } },
+              { label: 'l2', value: 'v2', customProperties: { prop2: false } },
+            ];
+          };
+          expect(choice._store.choices.length).to.equal(0);
+          const promise = choice.setChoices(fetcher);
+          expect(fetcherCalled).to.be.true;
+          const res = await promise;
+          expect(res).to.equal(choice);
+          expect(handleLoadingStateSpy.callCount).to.equal(2);
+          expect(choice._store.choices[1].value).to.equal('v2');
+          expect(choice._store.choices[1].label).to.equal('l2');
+          expect(choice._store.choices[1].customProperties).to.deep.equal({
+            prop2: false,
+          });
+        });
+      });
+    });
+
+    describe('setValue', () => {
+      let setChoiceOrItemStub;
+      const values = [
+        'Value 1',
+        {
+          value: 'Value 2',
+        },
+      ];
+
+      beforeEach(() => {
+        setChoiceOrItemStub = stub();
+        instance._setChoiceOrItem = setChoiceOrItemStub;
+      });
+
+      afterEach(() => {
+        instance._setChoiceOrItem.reset();
+      });
+
+      describe('not already initialised', () => {
+        beforeEach(() => {
+          instance.initialised = false;
+          output = instance.setValue(values);
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('returns early', () => {
+          expect(setChoiceOrItemStub.called).to.equal(false);
+        });
+      });
+
+      describe('when already initialised', () => {
+        beforeEach(() => {
+          instance.initialised = true;
+          output = instance.setValue(values);
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('sets each value', () => {
+          expect(setChoiceOrItemStub.callCount).to.equal(2);
+          expect(setChoiceOrItemStub.firstCall.args[0]).to.equal(values[0]);
+          expect(setChoiceOrItemStub.secondCall.args[0]).to.equal(values[1]);
+        });
+      });
+    });
+
+    describe('setChoiceByValue', () => {
+      let findAndSelectChoiceByValueStub;
+
+      beforeEach(() => {
+        findAndSelectChoiceByValueStub = stub();
+        instance._findAndSelectChoiceByValue = findAndSelectChoiceByValueStub;
+      });
+
+      afterEach(() => {
+        instance._findAndSelectChoiceByValue.reset();
+      });
+
+      describe('not already initialised', () => {
+        beforeEach(() => {
+          instance.initialised = false;
+          output = instance.setChoiceByValue([]);
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('returns early', () => {
+          expect(findAndSelectChoiceByValueStub.called).to.equal(false);
+        });
+      });
+
+      describe('when already initialised and not text element', () => {
+        beforeEach(() => {
+          instance.initialised = true;
+          instance._isTextElement = false;
+        });
+
+        describe('passing a string value', () => {
+          const value = 'Test value';
+
+          beforeEach(() => {
+            output = instance.setChoiceByValue(value);
+          });
+
+          it('returns this', () => {
+            expect(output).to.eql(instance);
+          });
+
+          it('sets each choice with same value', () => {
+            expect(findAndSelectChoiceByValueStub.called).to.equal(true);
+            expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal(
+              value,
+            );
+          });
+        });
+
+        describe('passing an array of values', () => {
+          const values = ['Value 1', 'Value 2'];
+
+          beforeEach(() => {
+            output = instance.setChoiceByValue(values);
+          });
+
+          it('returns this', () => {
+            expect(output).to.eql(instance);
+          });
+
+          it('sets each choice with same value', () => {
+            expect(findAndSelectChoiceByValueStub.callCount).to.equal(2);
+            expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal(
+              values[0],
+            );
+            expect(findAndSelectChoiceByValueStub.secondCall.args[0]).to.equal(
+              values[1],
+            );
+          });
+        });
+      });
+    });
+
+    describe('getValue', () => {
+      let activeItemsStub;
+      const items = [
+        {
+          id: '1',
+          value: 'Test value 1',
+        },
+        {
+          id: '2',
+          value: 'Test value 2',
+        },
+      ];
+
+      beforeEach(() => {
+        activeItemsStub = stub(instance._store, 'activeItems').get(() => items);
+      });
+
+      afterEach(() => {
+        activeItemsStub.reset();
+      });
+
+      describe('passing true valueOnly flag', () => {
+        describe('select one input', () => {
+          beforeEach(() => {
+            instance._isSelectOneElement = true;
+            output = instance.getValue(true);
+          });
+
+          it('returns a single action value', () => {
+            expect(output).to.equal(items[0].value);
+          });
+        });
+
+        describe('non select one input', () => {
+          beforeEach(() => {
+            instance._isSelectOneElement = false;
+            output = instance.getValue(true);
+          });
+
+          it('returns all active item values', () => {
+            expect(output).to.eql(items.map((item) => item.value));
+          });
+        });
+      });
+
+      describe('passing false valueOnly flag', () => {
+        describe('select one input', () => {
+          beforeEach(() => {
+            instance._isSelectOneElement = true;
+            output = instance.getValue(false);
+          });
+
+          it('returns a single active item', () => {
+            expect(output).to.equal(items[0]);
+          });
+        });
+
+        describe('non select one input', () => {
+          beforeEach(() => {
+            instance._isSelectOneElement = false;
+            output = instance.getValue(false);
+          });
+
+          it('returns all active items', () => {
+            expect(output).to.eql(items);
+          });
+        });
+      });
+    });
+
+    describe('removeActiveItemsByValue', () => {
+      let activeItemsStub;
+      let removeItemStub;
+      const value = 'Removed';
+      const items = [
+        {
+          id: '1',
+          value: 'Not removed',
+        },
+        {
+          id: '2',
+          value: 'Removed',
+        },
+        {
+          id: '3',
+          value: 'Removed',
+        },
+      ];
+
+      beforeEach(() => {
+        removeItemStub = stub();
+        activeItemsStub = stub(instance._store, 'activeItems').get(() => items);
+        instance._removeItem = removeItemStub;
+
+        output = instance.removeActiveItemsByValue(value);
+      });
+
+      afterEach(() => {
+        activeItemsStub.reset();
+        instance._removeItem.reset();
+      });
+
+      it('removes each active item in store with matching value', () => {
+        expect(removeItemStub.callCount).to.equal(2);
+        expect(removeItemStub.firstCall.args[0]).to.equal(items[1]);
+        expect(removeItemStub.secondCall.args[0]).to.equal(items[2]);
+      });
+    });
+
+    describe('removeActiveItems', () => {
+      let activeItemsStub;
+      let removeItemStub;
+      const items = [
+        {
+          id: '1',
+          value: 'Not removed',
+        },
+        {
+          id: '2',
+          value: 'Removed',
+        },
+        {
+          id: '3',
+          value: 'Removed',
+        },
+      ];
+
+      beforeEach(() => {
+        removeItemStub = stub();
+        activeItemsStub = stub(instance._store, 'activeItems').get(() => items);
+        instance._removeItem = removeItemStub;
+      });
+
+      afterEach(() => {
+        activeItemsStub.reset();
+        instance._removeItem.reset();
+      });
+
+      describe('not passing id to exclude', () => {
+        beforeEach(() => {
+          output = instance.removeActiveItems();
+        });
+
+        it('removes all active items in store', () => {
+          expect(removeItemStub.callCount).to.equal(items.length);
+          expect(removeItemStub.firstCall.args[0]).to.equal(items[0]);
+          expect(removeItemStub.secondCall.args[0]).to.equal(items[1]);
+          expect(removeItemStub.thirdCall.args[0]).to.equal(items[2]);
+        });
+      });
+
+      describe('passing id to exclude', () => {
+        const idToExclude = '2';
+
+        beforeEach(() => {
+          output = instance.removeActiveItems(idToExclude);
+        });
+
+        it('removes all active items in store with id that does match excludedId', () => {
+          expect(removeItemStub.callCount).to.equal(2);
+          expect(removeItemStub.firstCall.args[0]).to.equal(items[0]);
+          expect(removeItemStub.secondCall.args[0]).to.equal(items[2]);
+        });
+      });
+    });
+
+    describe('removeHighlightedItems', () => {
+      let highlightedActiveItemsStub;
+      let removeItemStub;
+      let triggerChangeStub;
+
+      const items = [
+        {
+          id: 1,
+          value: 'Test 1',
+        },
+        {
+          id: 2,
+          value: 'Test 2',
+        },
+      ];
+
+      beforeEach(() => {
+        highlightedActiveItemsStub = stub(
+          instance._store,
+          'highlightedActiveItems',
+        ).get(() => items);
+        removeItemStub = stub();
+        triggerChangeStub = stub();
+
+        instance._removeItem = removeItemStub;
+        instance._triggerChange = triggerChangeStub;
+      });
+
+      afterEach(() => {
+        highlightedActiveItemsStub.reset();
+        instance._removeItem.reset();
+        instance._triggerChange.reset();
+      });
+
+      describe('runEvent parameter being passed', () => {
+        beforeEach(() => {
+          output = instance.removeHighlightedItems();
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('removes each highlighted item in store', () => {
+          expect(removeItemStub.callCount).to.equal(2);
+        });
+      });
+
+      describe('runEvent parameter not being passed', () => {
+        beforeEach(() => {
+          output = instance.removeHighlightedItems(true);
+        });
+
+        it('returns this', () => {
+          expect(output).to.eql(instance);
+        });
+
+        it('triggers event with item value', () => {
+          expect(triggerChangeStub.callCount).to.equal(2);
+          expect(triggerChangeStub.firstCall.args[0]).to.equal(items[0].value);
+          expect(triggerChangeStub.secondCall.args[0]).to.equal(items[1].value);
+        });
+      });
+    });
+
+    describe('setChoices', () => {
+      let clearChoicesStub;
+      let addGroupStub;
+      let addChoiceStub;
+      let containerOuterRemoveLoadingStateStub;
+      const value = 'value';
+      const label = 'label';
+      const choices: Choice[] = [
+        {
+          id: 1,
+          value: '1',
+          label: 'Test 1',
+          selected: false,
+          disabled: false,
+        },
+        {
+          id: 2,
+          value: '2',
+          label: 'Test 2',
+          selected: false,
+          disabled: true,
+        },
+      ];
+      const groups: Group[] = [
+        {
+          ...choices[0],
+          choices,
+        },
+        choices[1],
+      ];
+
+      beforeEach(() => {
+        clearChoicesStub = stub();
+        addGroupStub = stub();
+        addChoiceStub = stub();
+        containerOuterRemoveLoadingStateStub = stub();
+
+        instance.clearChoices = clearChoicesStub;
+        instance._addGroup = addGroupStub;
+        instance._addChoice = addChoiceStub;
+        instance.containerOuter.removeLoadingState =
+          containerOuterRemoveLoadingStateStub;
+      });
+
+      afterEach(() => {
+        instance.clearChoices.reset();
+        instance._addGroup.reset();
+        instance._addChoice.reset();
+        instance.containerOuter.removeLoadingState.reset();
+      });
+
+      describe('when element is not select element', () => {
+        beforeEach(() => {
+          instance._isSelectElement = false;
+        });
+
+        it('throws', () => {
+          expect(() =>
+            instance.setChoices(choices, value, label, false),
+          ).to.throw(TypeError, /input/i);
+        });
+      });
+
+      describe('passing invalid arguments', () => {
+        describe('passing no value', () => {
+          beforeEach(() => {
+            instance._isSelectElement = true;
+          });
+
+          it('throws', () => {
+            expect(() =>
+              instance.setChoices(choices, null, 'label', false),
+            ).to.throw(TypeError, /value/i);
+          });
+        });
+      });
+
+      describe('passing valid arguments', () => {
+        beforeEach(() => {
+          instance._isSelectElement = true;
+        });
+
+        it('removes loading state', () => {
+          instance.setChoices(choices, value, label, false);
+          expect(containerOuterRemoveLoadingStateStub.called).to.equal(true);
+        });
+
+        describe('passing choices with children choices', () => {
+          it('adds groups', () => {
+            instance.setChoices(groups, value, label, false);
+            expect(addGroupStub.callCount).to.equal(1);
+            expect(addGroupStub.firstCall.args[0]).to.eql({
+              group: groups[0],
+              id: groups[0].id,
+              valueKey: value,
+              labelKey: label,
+            });
+          });
+        });
+
+        describe('passing choices without children choices', () => {
+          it('adds passed choices', () => {
+            instance.setChoices(choices, value, label, false);
+            expect(addChoiceStub.callCount).to.equal(2);
+            addChoiceStub.getCalls().forEach((call, index) => {
+              expect(call.args[0]).to.eql({
+                value: choices[index][value],
+                label: choices[index][label],
+                isSelected: !!choices[index].selected,
+                isDisabled: !!choices[index].disabled,
+                customProperties: choices[index].customProperties,
+                placeholder: !!choices[index].placeholder,
+              });
+            });
+          });
+        });
+
+        describe('passing an empty array with a true replaceChoices flag', () => {
+          it('choices are cleared', () => {
+            instance._isSelectElement = true;
+            instance.setChoices([], value, label, true);
+            expect(clearChoicesStub.called).to.equal(true);
+          });
+        });
+
+        describe('passing an empty array with a false replaceChoices flag', () => {
+          it('choices stay the same', () => {
+            instance._isSelectElement = true;
+            instance.setChoices([], value, label, false);
+            expect(clearChoicesStub.called).to.equal(false);
+          });
+        });
+
+        describe('passing true replaceChoices flag', () => {
+          it('choices are cleared', () => {
+            instance.setChoices(choices, value, label, true);
+            expect(clearChoicesStub.called).to.equal(true);
+          });
+        });
+
+        describe('passing false replaceChoices flag', () => {
+          it('choices are not cleared', () => {
+            instance.setChoices(choices, value, label, false);
+            expect(clearChoicesStub.called).to.equal(false);
+          });
+        });
+      });
+    });
+  });
+
+  describe('events', () => {
+    describe('search', () => {
+      const choices: Choice[] = [
+        {
+          id: 1,
+          value: '1',
+          label: 'Test 1',
+          selected: false,
+          disabled: false,
+        },
+        {
+          id: 2,
+          value: '2',
+          label: 'Test 2',
+          selected: false,
+          disabled: false,
+        },
+      ];
+
+      beforeEach(() => {
+        document.body.innerHTML = `
+        <select data-choice multiple></select>
+        `;
+
+        instance = new Choices('[data-choice]', {
+          choices,
+          allowHTML: false,
+          searchEnabled: true,
+        });
+      });
+
+      it('details are passed', (done) => {
+        const query =
+          'This is a <search> query & a "test" with characters that should not be sanitised.';
+
+        instance.input.value = query;
+        instance.input.focus();
+        instance.passedElement.element.addEventListener(
+          'search',
+          (event) => {
+            expect(event.detail).to.eql({
+              value: query,
+              resultCount: 0,
+            });
+            done();
+          },
+          { once: true },
+        );
+
+        instance._onKeyUp({ target: null, keyCode: null });
+      });
+
+      it('uses Fuse options', (done) => {
+        instance.input.value = 'test';
+        instance.input.focus();
+        instance.passedElement.element.addEventListener(
+          'search',
+          (event) => {
+            expect(event.detail.resultCount).to.eql(2);
+
+            instance.config.fuseOptions.isCaseSensitive = true;
+            instance.config.fuseOptions.minMatchCharLength = 4;
+            instance.passedElement.element.addEventListener(
+              'search',
+              (eventCaseSensitive) => {
+                expect(eventCaseSensitive.detail.resultCount).to.eql(0);
+                done();
+              },
+              { once: true },
+            );
+
+            instance._onKeyUp({ target: null, keyCode: null });
+          },
+          { once: true },
+        );
+
+        instance._onKeyUp({ target: null, keyCode: null });
+      });
+
+      it('is fired with a searchFloor of 0', (done) => {
+        instance.config.searchFloor = 0;
+        instance.input.value = '';
+        instance.input.focus();
+        instance.passedElement.element.addEventListener('search', (event) => {
+          expect(event.detail).to.eql({
+            value: instance.input.value,
+            resultCount: 0,
+          });
+          done();
+        });
+
+        instance._onKeyUp({ target: null, keyCode: null });
+      });
+    });
+  });
+
+  describe('private methods', () => {
+    describe('_createGroupsFragment', () => {
+      let _createChoicesFragmentStub;
+      const choices: Choice[] = [
+        {
+          id: 1,
+          selected: true,
+          groupId: 1,
+          value: 'Choice 1',
+          label: 'Choice 1',
+        },
+        {
+          id: 2,
+          selected: false,
+          groupId: 2,
+          value: 'Choice 2',
+          label: 'Choice 2',
+        },
+        {
+          id: 3,
+          selected: false,
+          groupId: 1,
+          value: 'Choice 3',
+          label: 'Choice 3',
+        },
+      ];
+
+      const groups: Group[] = [
+        {
+          id: 2,
+          value: 'Group 2',
+          active: true,
+          disabled: false,
+        },
+        {
+          id: 1,
+          value: 'Group 1',
+          active: true,
+          disabled: false,
+        },
+      ];
+
+      beforeEach(() => {
+        _createChoicesFragmentStub = stub();
+        instance._createChoicesFragment = _createChoicesFragmentStub;
+      });
+
+      afterEach(() => {
+        instance._createChoicesFragment.reset();
+      });
+
+      describe('returning a fragment of groups', () => {
+        describe('passing fragment argument', () => {
+          it('updates fragment with groups', () => {
+            const fragment = document.createDocumentFragment();
+            const childElement = document.createElement('div');
+            fragment.appendChild(childElement);
+
+            output = instance._createGroupsFragment(groups, choices, fragment);
+            const elementToWrapFragment = document.createElement('div');
+            elementToWrapFragment.appendChild(output);
+
+            expect(output).to.be.instanceOf(DocumentFragment);
+            expect(elementToWrapFragment.children[0]).to.eql(childElement);
+            expect(
+              elementToWrapFragment.querySelectorAll('[data-group]').length,
+            ).to.equal(2);
+          });
+        });
+
+        describe('not passing fragment argument', () => {
+          it('returns new groups fragment', () => {
+            output = instance._createGroupsFragment(groups, choices);
+            const elementToWrapFragment = document.createElement('div');
+            elementToWrapFragment.appendChild(output);
+
+            expect(output).to.be.instanceOf(DocumentFragment);
+            expect(
+              elementToWrapFragment.querySelectorAll('[data-group]').length,
+            ).to.equal(2);
+          });
+        });
+
+        describe('sorting groups', () => {
+          let sortFnStub;
+
+          beforeEach(() => {
+            sortFnStub = stub();
+            instance.config.sorter = sortFnStub;
+            instance.config.shouldSort = true;
+          });
+
+          afterEach(() => {
+            instance.config.sorter.reset();
+          });
+
+          it('sorts groups by config.sorter', () => {
+            expect(sortFnStub.called).to.equal(false);
+            instance._createGroupsFragment(groups, choices);
+            expect(sortFnStub.called).to.equal(true);
+          });
+        });
+
+        describe('not sorting groups', () => {
+          let sortFnStub;
+
+          beforeEach(() => {
+            sortFnStub = stub();
+            instance.config.sorter = sortFnStub;
+            instance.config.shouldSort = false;
+          });
+
+          afterEach(() => {
+            instance.config.sorter.reset();
+          });
+
+          it('does not sort groups', () => {
+            instance._createGroupsFragment(groups, choices);
+            expect(sortFnStub.called).to.equal(false);
+          });
+        });
+
+        describe('select-one element', () => {
+          beforeEach(() => {
+            instance._isSelectOneElement = true;
+          });
+
+          it('calls _createChoicesFragment with choices that belong to each group', () => {
+            expect(_createChoicesFragmentStub.called).to.equal(false);
+            instance._createGroupsFragment(groups, choices);
+            expect(_createChoicesFragmentStub.called).to.equal(true);
+            expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([
+              {
+                id: 1,
+                selected: true,
+                groupId: 1,
+                value: 'Choice 1',
+                label: 'Choice 1',
+              },
+              {
+                id: 3,
+                selected: false,
+                groupId: 1,
+                value: 'Choice 3',
+                label: 'Choice 3',
+              },
+            ]);
+            expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([
+              {
+                id: 2,
+                selected: false,
+                groupId: 2,
+                value: 'Choice 2',
+                label: 'Choice 2',
+              },
+            ]);
+          });
+        });
+
+        describe('text/select-multiple element', () => {
+          describe('renderSelectedChoices set to "always"', () => {
+            beforeEach(() => {
+              instance._isSelectOneElement = false;
+              instance.config.renderSelectedChoices = 'always';
+            });
+
+            it('calls _createChoicesFragment with choices that belong to each group', () => {
+              expect(_createChoicesFragmentStub.called).to.equal(false);
+              instance._createGroupsFragment(groups, choices);
+              expect(_createChoicesFragmentStub.called).to.equal(true);
+              expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([
+                {
+                  id: 1,
+                  selected: true,
+                  groupId: 1,
+                  value: 'Choice 1',
+                  label: 'Choice 1',
+                },
+                {
+                  id: 3,
+                  selected: false,
+                  groupId: 1,
+                  value: 'Choice 3',
+                  label: 'Choice 3',
+                },
+              ]);
+              expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([
+                {
+                  id: 2,
+                  selected: false,
+                  groupId: 2,
+                  value: 'Choice 2',
+                  label: 'Choice 2',
+                },
+              ]);
+            });
+          });
+
+          describe('renderSelectedChoices not set to "always"', () => {
+            beforeEach(() => {
+              instance._isSelectOneElement = false;
+              instance.config.renderSelectedChoices = false;
+            });
+
+            it('calls _createChoicesFragment with choices that belong to each group that are not already selected', () => {
+              expect(_createChoicesFragmentStub.called).to.equal(false);
+              instance._createGroupsFragment(groups, choices);
+              expect(_createChoicesFragmentStub.called).to.equal(true);
+              expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([
+                {
+                  id: 3,
+                  selected: false,
+                  groupId: 1,
+                  value: 'Choice 3',
+                  label: 'Choice 3',
+                },
+              ]);
+              expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([
+                {
+                  id: 2,
+                  selected: false,
+                  groupId: 2,
+                  value: 'Choice 2',
+                  label: 'Choice 2',
+                },
+              ]);
+            });
+          });
+        });
+      });
+    });
+
+    describe('_generatePlaceholderValue', () => {
+      describe('select element', () => {
+        describe('when a placeholder option is defined', () => {
+          it('returns the text value of the placeholder option', () => {
+            const placeholderValue = 'I am a placeholder';
+
+            instance._isSelectElement = true;
+            instance.passedElement.placeholderOption = {
+              text: placeholderValue,
+            };
+
+            const value = instance._generatePlaceholderValue();
+            expect(value).to.equal(placeholderValue);
+          });
+        });
+
+        describe('when a placeholder option is not defined', () => {
+          it('returns null', () => {
+            instance._isSelectElement = true;
+            instance.passedElement.placeholderOption = undefined;
+
+            const value = instance._generatePlaceholderValue();
+            expect(value).to.equal(null);
+          });
+        });
+      });
+
+      describe('text input', () => {
+        describe('when the placeholder config option is set to true', () => {
+          describe('when the placeholderValue config option is defined', () => {
+            it('returns placeholderValue', () => {
+              const placeholderValue = 'I am a placeholder';
+
+              instance._isSelectElement = false;
+              instance.config.placeholder = true;
+              instance.config.placeholderValue = placeholderValue;
+
+              const value = instance._generatePlaceholderValue();
+              expect(value).to.equal(placeholderValue);
+            });
+          });
+
+          describe('when the placeholderValue config option is not defined', () => {
+            describe('when the placeholder attribute is defined on the passed element', () => {
+              it('returns the value of the placeholder attribute', () => {
+                const placeholderValue = 'I am a placeholder';
+
+                instance._isSelectElement = false;
+                instance.config.placeholder = true;
+                instance.config.placeholderValue = undefined;
+                instance.passedElement.element = {
+                  dataset: {
+                    placeholder: placeholderValue,
+                  },
+                };
+
+                const value = instance._generatePlaceholderValue();
+                expect(value).to.equal(placeholderValue);
+              });
+            });
+
+            describe('when the placeholder attribute is not defined on the passed element', () => {
+              it('returns null', () => {
+                instance._isSelectElement = false;
+                instance.config.placeholder = true;
+                instance.config.placeholderValue = undefined;
+                instance.passedElement.element = {
+                  dataset: {
+                    placeholder: undefined,
+                  },
+                };
+
+                const value = instance._generatePlaceholderValue();
+                expect(value).to.equal(null);
+              });
+            });
+          });
+        });
+
+        describe('when the placeholder config option is set to false', () => {
+          it('returns null', () => {
+            instance._isSelectElement = false;
+            instance.config.placeholder = false;
+
+            const value = instance._generatePlaceholderValue();
+            expect(value).to.equal(null);
+          });
+        });
+      });
+    });
+
+    describe('_getTemplate', () => {
+      describe('when passing a template key', () => {
+        it('returns the generated template for the given template key', () => {
+          const templateKey = 'test';
+          const element = document.createElement('div');
+          const customArg = { test: true };
+
+          instance._templates = {
+            [templateKey]: stub().returns(element),
+          };
+
+          output = instance._getTemplate(templateKey, customArg);
+          expect(output).to.deep.equal(element);
+          expect(instance._templates[templateKey]).to.have.been.calledOnceWith(
+            instance.config,
+            customArg,
+          );
+        });
+      });
+    });
+
+    describe('_onKeyDown', () => {
+      let activeItems;
+      let hasItems;
+      let hasActiveDropdown;
+      let hasFocussedInput;
+
+      beforeEach(() => {
+        instance.showDropdown = stub();
+        instance._onSelectKey = stub();
+        instance._onEnterKey = stub();
+        instance._onEscapeKey = stub();
+        instance._onDirectionKey = stub();
+        instance._onDeleteKey = stub();
+
+        ({ activeItems } = instance._store);
+        hasItems = instance.itemList.hasChildren();
+        hasActiveDropdown = instance.dropdown.isActive;
+        hasFocussedInput = instance.input.isFocussed;
+      });
+
+      describe('direction key', () => {
+        const keyCodes = [
+          KEY_CODES.UP_KEY,
+          KEY_CODES.DOWN_KEY,
+          KEY_CODES.PAGE_UP_KEY,
+          KEY_CODES.PAGE_DOWN_KEY,
+        ];
+
+        keyCodes.forEach((keyCode) => {
+          it(`calls _onDirectionKey with the expected arguments`, () => {
+            const event = {
+              keyCode,
+            };
+
+            instance._onKeyDown(event);
+
+            expect(instance._onDirectionKey).to.have.been.calledWith(
+              event,
+              hasActiveDropdown,
+            );
+          });
+        });
+      });
+
+      describe('select key', () => {
+        it(`calls _onSelectKey with the expected arguments`, () => {
+          const event = {
+            keyCode: KEY_CODES.A_KEY,
+          };
+
+          instance._onKeyDown(event);
+
+          expect(instance._onSelectKey).to.have.been.calledWith(
+            event,
+            hasItems,
+          );
+        });
+      });
+
+      describe('enter key', () => {
+        it(`calls _onEnterKey with the expected arguments`, () => {
+          const event = {
+            keyCode: KEY_CODES.ENTER_KEY,
+          };
+
+          instance._onKeyDown(event);
+
+          expect(instance._onEnterKey).to.have.been.calledWith(
+            event,
+            activeItems,
+            hasActiveDropdown,
+          );
+        });
+      });
+
+      describe('delete key', () => {
+        const keyCodes = [KEY_CODES.DELETE_KEY, KEY_CODES.BACK_KEY];
+
+        keyCodes.forEach((keyCode) => {
+          it(`calls _onDeleteKey with the expected arguments`, () => {
+            const event = {
+              keyCode,
+            };
+
+            instance._onKeyDown(event);
+
+            expect(instance._onDeleteKey).to.have.been.calledWith(
+              event,
+              activeItems,
+              hasFocussedInput,
+            );
+          });
+        });
+      });
+    });
+
+    describe('_removeItem', () => {
+      beforeEach(() => {
+        instance._store.dispatch = stub();
+      });
+
+      afterEach(() => {
+        instance._store.dispatch.reset();
+      });
+
+      describe('when given an item to remove', () => {
+        const item = {
+          id: 1111,
+          value: 'test value',
+          label: 'test label',
+          choiceId: 2222,
+          groupId: 3333,
+          customProperties: {},
+        };
+
+        it('dispatches a REMOVE_ITEM action to the store', () => {
+          instance._removeItem(item);
+
+          expect(instance._store.dispatch).to.have.been.calledWith(
+            removeItem(item.id, item.choiceId),
+          );
+        });
+
+        it('triggers a REMOVE_ITEM event on the passed element', (done) => {
+          passedElement.addEventListener(
+            'removeItem',
+            (event) => {
+              expect(event.detail).to.eql({
+                id: item.id,
+                value: item.value,
+                label: item.label,
+                customProperties: item.customProperties,
+                groupValue: null,
+              });
+              done();
+            },
+            false,
+          );
+
+          instance._removeItem(item);
+        });
+
+        describe('when the item belongs to a group', () => {
+          const group = {
+            id: 1,
+            value: 'testing',
+          };
+          const itemWithGroup = {
+            ...item,
+            groupId: group.id,
+          };
+
+          beforeEach(() => {
+            instance._store.getGroupById = stub();
+            instance._store.getGroupById.returns(group);
+          });
+
+          afterEach(() => {
+            instance._store.getGroupById.reset();
+          });
+
+          it("includes the group's value in the triggered event", (done) => {
+            passedElement.addEventListener(
+              'removeItem',
+              (event) => {
+                expect(event.detail).to.eql({
+                  id: itemWithGroup.id,
+                  value: itemWithGroup.value,
+                  label: itemWithGroup.label,
+                  customProperties: itemWithGroup.customProperties,
+                  groupValue: group.value,
+                });
+
+                done();
+              },
+              false,
+            );
+
+            instance._removeItem(itemWithGroup);
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/choices.ts b/my_flask_app/static/choices.js/src/scripts/choices.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36a51da407d247cc86d5bc8e0675856432337c75
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/choices.ts
@@ -0,0 +1,2438 @@
+import merge from 'deepmerge';
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import Fuse from 'fuse.js';
+
+import {
+  activateChoices,
+  addChoice,
+  clearChoices,
+  filterChoices,
+  Result,
+} from './actions/choices';
+import { addGroup } from './actions/groups';
+import { addItem, highlightItem, removeItem } from './actions/items';
+import { clearAll, resetTo, setIsLoading } from './actions/misc';
+import {
+  Container,
+  Dropdown,
+  Input,
+  List,
+  WrappedInput,
+  WrappedSelect,
+} from './components';
+import {
+  EVENTS,
+  KEY_CODES,
+  SELECT_MULTIPLE_TYPE,
+  SELECT_ONE_TYPE,
+  TEXT_TYPE,
+} from './constants';
+import { DEFAULT_CONFIG } from './defaults';
+import { Choice } from './interfaces/choice';
+import { Group } from './interfaces/group';
+import { Item } from './interfaces/item';
+import { Notice } from './interfaces/notice';
+import { Options } from './interfaces/options';
+import { PassedElement } from './interfaces/passed-element';
+import { State } from './interfaces/state';
+
+import {
+  diff,
+  existsInArray,
+  generateId,
+  getAdjacentEl,
+  getType,
+  isScrolledIntoView,
+  isType,
+  sortByScore,
+  strToEl,
+  parseCustomProperties,
+} from './lib/utils';
+import { defaultState } from './reducers';
+import Store from './store/store';
+import templates from './templates';
+
+/** @see {@link http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c} */
+const IS_IE11 =
+  '-ms-scroll-limit' in document.documentElement.style &&
+  '-ms-ime-align' in document.documentElement.style;
+
+const USER_DEFAULTS: Partial<Options> = {};
+
+/**
+ * Choices
+ * @author Josh Johnson<josh@joshuajohnson.co.uk>
+ */
+class Choices implements Choices {
+  static get defaults(): {
+    options: Partial<Options>;
+    templates: typeof templates;
+  } {
+    return Object.preventExtensions({
+      get options(): Partial<Options> {
+        return USER_DEFAULTS;
+      },
+      get templates(): typeof templates {
+        return templates;
+      },
+    });
+  }
+
+  initialised: boolean;
+
+  config: Options;
+
+  passedElement: WrappedInput | WrappedSelect;
+
+  containerOuter: Container;
+
+  containerInner: Container;
+
+  choiceList: List;
+
+  itemList: List;
+
+  input: Input;
+
+  dropdown: Dropdown;
+
+  _isTextElement: boolean;
+
+  _isSelectOneElement: boolean;
+
+  _isSelectMultipleElement: boolean;
+
+  _isSelectElement: boolean;
+
+  _store: Store;
+
+  _templates: typeof templates;
+
+  _initialState: State;
+
+  _currentState: State;
+
+  _prevState: State;
+
+  _currentValue: string;
+
+  _canSearch: boolean;
+
+  _isScrollingOnIe: boolean;
+
+  _highlightPosition: number;
+
+  _wasTap: boolean;
+
+  _isSearching: boolean;
+
+  _placeholderValue: string | null;
+
+  _baseId: string;
+
+  _direction: HTMLElement['dir'];
+
+  _idNames: {
+    itemChoice: string;
+  };
+
+  _presetGroups: Group[] | HTMLOptGroupElement[] | Element[];
+
+  _presetOptions: Item[] | HTMLOptionElement[];
+
+  _presetChoices: Partial<Choice>[];
+
+  _presetItems: Item[] | string[];
+
+  constructor(
+    element:
+      | string
+      | Element
+      | HTMLInputElement
+      | HTMLSelectElement = '[data-choice]',
+    userConfig: Partial<Options> = {},
+  ) {
+    if (userConfig.allowHTML === undefined) {
+      console.warn(
+        'Deprecation warning: allowHTML will default to false in a future release. To render HTML in Choices, you will need to set it to true. Setting allowHTML will suppress this message.',
+      );
+    }
+
+    this.config = merge.all<Options>(
+      [DEFAULT_CONFIG, Choices.defaults.options, userConfig],
+      // When merging array configs, replace with a copy of the userConfig array,
+      // instead of concatenating with the default array
+      { arrayMerge: (_, sourceArray) => [...sourceArray] },
+    );
+
+    const invalidConfigOptions = diff(this.config, DEFAULT_CONFIG);
+    if (invalidConfigOptions.length) {
+      console.warn(
+        'Unknown config option(s) passed',
+        invalidConfigOptions.join(', '),
+      );
+    }
+
+    const passedElement =
+      typeof element === 'string' ? document.querySelector(element) : element;
+
+    if (
+      !(
+        passedElement instanceof HTMLInputElement ||
+        passedElement instanceof HTMLSelectElement
+      )
+    ) {
+      throw TypeError(
+        'Expected one of the following types text|select-one|select-multiple',
+      );
+    }
+
+    this._isTextElement = passedElement.type === TEXT_TYPE;
+    this._isSelectOneElement = passedElement.type === SELECT_ONE_TYPE;
+    this._isSelectMultipleElement = passedElement.type === SELECT_MULTIPLE_TYPE;
+    this._isSelectElement =
+      this._isSelectOneElement || this._isSelectMultipleElement;
+
+    this.config.searchEnabled =
+      this._isSelectMultipleElement || this.config.searchEnabled;
+
+    if (!['auto', 'always'].includes(`${this.config.renderSelectedChoices}`)) {
+      this.config.renderSelectedChoices = 'auto';
+    }
+
+    if (
+      userConfig.addItemFilter &&
+      typeof userConfig.addItemFilter !== 'function'
+    ) {
+      const re =
+        userConfig.addItemFilter instanceof RegExp
+          ? userConfig.addItemFilter
+          : new RegExp(userConfig.addItemFilter);
+
+      this.config.addItemFilter = re.test.bind(re);
+    }
+
+    if (this._isTextElement) {
+      this.passedElement = new WrappedInput({
+        element: passedElement as HTMLInputElement,
+        classNames: this.config.classNames,
+        delimiter: this.config.delimiter,
+      });
+    } else {
+      this.passedElement = new WrappedSelect({
+        element: passedElement as HTMLSelectElement,
+        classNames: this.config.classNames,
+        template: (data: Item): HTMLOptionElement =>
+          this._templates.option(data),
+      });
+    }
+
+    this.initialised = false;
+
+    this._store = new Store();
+    this._initialState = defaultState;
+    this._currentState = defaultState;
+    this._prevState = defaultState;
+    this._currentValue = '';
+    this._canSearch = !!this.config.searchEnabled;
+    this._isScrollingOnIe = false;
+    this._highlightPosition = 0;
+    this._wasTap = true;
+    this._placeholderValue = this._generatePlaceholderValue();
+    this._baseId = generateId(this.passedElement.element, 'choices-');
+
+    /**
+     * setting direction in cases where it's explicitly set on passedElement
+     * or when calculated direction is different from the document
+     */
+    this._direction = this.passedElement.dir;
+
+    if (!this._direction) {
+      const { direction: elementDirection } = window.getComputedStyle(
+        this.passedElement.element,
+      );
+      const { direction: documentDirection } = window.getComputedStyle(
+        document.documentElement,
+      );
+      if (elementDirection !== documentDirection) {
+        this._direction = elementDirection;
+      }
+    }
+
+    this._idNames = {
+      itemChoice: 'item-choice',
+    };
+
+    if (this._isSelectElement) {
+      // Assign preset groups from passed element
+      this._presetGroups = (this.passedElement as WrappedSelect).optionGroups;
+      // Assign preset options from passed element
+      this._presetOptions = (this.passedElement as WrappedSelect).options;
+    }
+
+    // Assign preset choices from passed object
+    this._presetChoices = this.config.choices;
+    // Assign preset items from passed object first
+    this._presetItems = this.config.items;
+    // Add any values passed from attribute
+    if (this.passedElement.value && this._isTextElement) {
+      const splitValues: string[] = this.passedElement.value.split(
+        this.config.delimiter,
+      );
+      this._presetItems = (this._presetItems as string[]).concat(splitValues);
+    }
+    // Create array of choices from option elements
+    if ((this.passedElement as WrappedSelect).options) {
+      (this.passedElement as WrappedSelect).options.forEach((option) => {
+        this._presetChoices.push({
+          value: option.value,
+          label: option.innerHTML,
+          selected: !!option.selected,
+          disabled: option.disabled || option.parentNode.disabled,
+          placeholder:
+            option.value === '' || option.hasAttribute('placeholder'),
+          customProperties: parseCustomProperties(
+            option.dataset.customProperties,
+          ),
+        });
+      });
+    }
+
+    this._render = this._render.bind(this);
+    this._onFocus = this._onFocus.bind(this);
+    this._onBlur = this._onBlur.bind(this);
+    this._onKeyUp = this._onKeyUp.bind(this);
+    this._onKeyDown = this._onKeyDown.bind(this);
+    this._onClick = this._onClick.bind(this);
+    this._onTouchMove = this._onTouchMove.bind(this);
+    this._onTouchEnd = this._onTouchEnd.bind(this);
+    this._onMouseDown = this._onMouseDown.bind(this);
+    this._onMouseOver = this._onMouseOver.bind(this);
+    this._onFormReset = this._onFormReset.bind(this);
+    this._onSelectKey = this._onSelectKey.bind(this);
+    this._onEnterKey = this._onEnterKey.bind(this);
+    this._onEscapeKey = this._onEscapeKey.bind(this);
+    this._onDirectionKey = this._onDirectionKey.bind(this);
+    this._onDeleteKey = this._onDeleteKey.bind(this);
+
+    // If element has already been initialised with Choices, fail silently
+    if (this.passedElement.isActive) {
+      if (!this.config.silent) {
+        console.warn(
+          'Trying to initialise Choices on element already initialised',
+          { element },
+        );
+      }
+
+      this.initialised = true;
+
+      return;
+    }
+
+    // Let's go
+    this.init();
+  }
+
+  init(): void {
+    if (this.initialised) {
+      return;
+    }
+
+    this._createTemplates();
+    this._createElements();
+    this._createStructure();
+
+    this._store.subscribe(this._render);
+
+    this._render();
+    this._addEventListeners();
+
+    const shouldDisable =
+      !this.config.addItems ||
+      this.passedElement.element.hasAttribute('disabled');
+
+    if (shouldDisable) {
+      this.disable();
+    }
+
+    this.initialised = true;
+
+    const { callbackOnInit } = this.config;
+    // Run callback if it is a function
+    if (callbackOnInit && typeof callbackOnInit === 'function') {
+      callbackOnInit.call(this);
+    }
+  }
+
+  destroy(): void {
+    if (!this.initialised) {
+      return;
+    }
+
+    this._removeEventListeners();
+    this.passedElement.reveal();
+    this.containerOuter.unwrap(this.passedElement.element);
+
+    this.clearStore();
+
+    if (this._isSelectElement) {
+      (this.passedElement as WrappedSelect).options = this._presetOptions;
+    }
+
+    this._templates = templates;
+    this.initialised = false;
+  }
+
+  enable(): this {
+    if (this.passedElement.isDisabled) {
+      this.passedElement.enable();
+    }
+
+    if (this.containerOuter.isDisabled) {
+      this._addEventListeners();
+      this.input.enable();
+      this.containerOuter.enable();
+    }
+
+    return this;
+  }
+
+  disable(): this {
+    if (!this.passedElement.isDisabled) {
+      this.passedElement.disable();
+    }
+
+    if (!this.containerOuter.isDisabled) {
+      this._removeEventListeners();
+      this.input.disable();
+      this.containerOuter.disable();
+    }
+
+    return this;
+  }
+
+  highlightItem(item: Item, runEvent = true): this {
+    if (!item || !item.id) {
+      return this;
+    }
+
+    const { id, groupId = -1, value = '', label = '' } = item;
+    const group = groupId >= 0 ? this._store.getGroupById(groupId) : null;
+
+    this._store.dispatch(highlightItem(id, true));
+
+    if (runEvent) {
+      this.passedElement.triggerEvent(EVENTS.highlightItem, {
+        id,
+        value,
+        label,
+        groupValue: group && group.value ? group.value : null,
+      });
+    }
+
+    return this;
+  }
+
+  unhighlightItem(item: Item): this {
+    if (!item || !item.id) {
+      return this;
+    }
+
+    const { id, groupId = -1, value = '', label = '' } = item;
+    const group = groupId >= 0 ? this._store.getGroupById(groupId) : null;
+
+    this._store.dispatch(highlightItem(id, false));
+    this.passedElement.triggerEvent(EVENTS.highlightItem, {
+      id,
+      value,
+      label,
+      groupValue: group && group.value ? group.value : null,
+    });
+
+    return this;
+  }
+
+  highlightAll(): this {
+    this._store.items.forEach((item) => this.highlightItem(item));
+
+    return this;
+  }
+
+  unhighlightAll(): this {
+    this._store.items.forEach((item) => this.unhighlightItem(item));
+
+    return this;
+  }
+
+  removeActiveItemsByValue(value: string): this {
+    this._store.activeItems
+      .filter((item) => item.value === value)
+      .forEach((item) => this._removeItem(item));
+
+    return this;
+  }
+
+  removeActiveItems(excludedId: number): this {
+    this._store.activeItems
+      .filter(({ id }) => id !== excludedId)
+      .forEach((item) => this._removeItem(item));
+
+    return this;
+  }
+
+  removeHighlightedItems(runEvent = false): this {
+    this._store.highlightedActiveItems.forEach((item) => {
+      this._removeItem(item);
+      // If this action was performed by the user
+      // trigger the event
+      if (runEvent) {
+        this._triggerChange(item.value);
+      }
+    });
+
+    return this;
+  }
+
+  showDropdown(preventInputFocus?: boolean): this {
+    if (this.dropdown.isActive) {
+      return this;
+    }
+
+    requestAnimationFrame(() => {
+      this.dropdown.show();
+      this.containerOuter.open(this.dropdown.distanceFromTopWindow);
+
+      if (!preventInputFocus && this._canSearch) {
+        this.input.focus();
+      }
+
+      this.passedElement.triggerEvent(EVENTS.showDropdown, {});
+    });
+
+    return this;
+  }
+
+  hideDropdown(preventInputBlur?: boolean): this {
+    if (!this.dropdown.isActive) {
+      return this;
+    }
+
+    requestAnimationFrame(() => {
+      this.dropdown.hide();
+      this.containerOuter.close();
+
+      if (!preventInputBlur && this._canSearch) {
+        this.input.removeActiveDescendant();
+        this.input.blur();
+      }
+
+      this.passedElement.triggerEvent(EVENTS.hideDropdown, {});
+    });
+
+    return this;
+  }
+
+  getValue(valueOnly = false): string[] | Item[] | Item | string {
+    const values = this._store.activeItems.reduce<any[]>(
+      (selectedItems, item) => {
+        const itemValue = valueOnly ? item.value : item;
+        selectedItems.push(itemValue);
+
+        return selectedItems;
+      },
+      [],
+    );
+
+    return this._isSelectOneElement ? values[0] : values;
+  }
+
+  setValue(items: string[] | Item[]): this {
+    if (!this.initialised) {
+      return this;
+    }
+
+    items.forEach((value) => this._setChoiceOrItem(value));
+
+    return this;
+  }
+
+  setChoiceByValue(value: string | string[]): this {
+    if (!this.initialised || this._isTextElement) {
+      return this;
+    }
+
+    // If only one value has been passed, convert to array
+    const choiceValue = Array.isArray(value) ? value : [value];
+
+    // Loop through each value and
+    choiceValue.forEach((val) => this._findAndSelectChoiceByValue(val));
+
+    return this;
+  }
+
+  /**
+   * Set choices of select input via an array of objects (or function that returns array of object or promise of it),
+   * a value field name and a label field name.
+   * This behaves the same as passing items via the choices option but can be called after initialising Choices.
+   * This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices.
+   * Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc).
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @example
+   * ```js
+   * const example = new Choices(element);
+   *
+   * example.setChoices([
+   *   {value: 'One', label: 'Label One', disabled: true},
+   *   {value: 'Two', label: 'Label Two', selected: true},
+   *   {value: 'Three', label: 'Label Three'},
+   * ], 'value', 'label', false);
+   * ```
+   *
+   * @example
+   * ```js
+   * const example = new Choices(element);
+   *
+   * example.setChoices(async () => {
+   *   try {
+   *      const items = await fetch('/items');
+   *      return items.json()
+   *   } catch(err) {
+   *      console.error(err)
+   *   }
+   * });
+   * ```
+   *
+   * @example
+   * ```js
+   * const example = new Choices(element);
+   *
+   * example.setChoices([{
+   *   label: 'Group one',
+   *   id: 1,
+   *   disabled: false,
+   *   choices: [
+   *     {value: 'Child One', label: 'Child One', selected: true},
+   *     {value: 'Child Two', label: 'Child Two',  disabled: true},
+   *     {value: 'Child Three', label: 'Child Three'},
+   *   ]
+   * },
+   * {
+   *   label: 'Group two',
+   *   id: 2,
+   *   disabled: false,
+   *   choices: [
+   *     {value: 'Child Four', label: 'Child Four', disabled: true},
+   *     {value: 'Child Five', label: 'Child Five'},
+   *     {value: 'Child Six', label: 'Child Six', customProperties: {
+   *       description: 'Custom description about child six',
+   *       random: 'Another random custom property'
+   *     }},
+   *   ]
+   * }], 'value', 'label', false);
+   * ```
+   */
+  setChoices(
+    choicesArrayOrFetcher:
+      | Choice[]
+      | Group[]
+      | ((instance: Choices) => Choice[] | Promise<Choice[]>) = [],
+    value = 'value',
+    label = 'label',
+    replaceChoices = false,
+  ): this | Promise<this> {
+    if (!this.initialised) {
+      throw new ReferenceError(
+        `setChoices was called on a non-initialized instance of Choices`,
+      );
+    }
+    if (!this._isSelectElement) {
+      throw new TypeError(`setChoices can't be used with INPUT based Choices`);
+    }
+
+    if (typeof value !== 'string' || !value) {
+      throw new TypeError(
+        `value parameter must be a name of 'value' field in passed objects`,
+      );
+    }
+
+    // Clear choices if needed
+    if (replaceChoices) {
+      this.clearChoices();
+    }
+
+    if (typeof choicesArrayOrFetcher === 'function') {
+      // it's a choices fetcher function
+      const fetcher = choicesArrayOrFetcher(this);
+
+      if (typeof Promise === 'function' && fetcher instanceof Promise) {
+        // that's a promise
+        // eslint-disable-next-line no-promise-executor-return
+        return new Promise((resolve) => requestAnimationFrame(resolve))
+          .then(() => this._handleLoadingState(true))
+          .then(() => fetcher)
+          .then((data: Choice[]) =>
+            this.setChoices(data, value, label, replaceChoices),
+          )
+          .catch((err) => {
+            if (!this.config.silent) {
+              console.error(err);
+            }
+          })
+          .then(() => this._handleLoadingState(false))
+          .then(() => this);
+      }
+
+      // function returned something else than promise, let's check if it's an array of choices
+      if (!Array.isArray(fetcher)) {
+        throw new TypeError(
+          `.setChoices first argument function must return either array of choices or Promise, got: ${typeof fetcher}`,
+        );
+      }
+
+      // recursion with results, it's sync and choices were cleared already
+      return this.setChoices(fetcher, value, label, false);
+    }
+
+    if (!Array.isArray(choicesArrayOrFetcher)) {
+      throw new TypeError(
+        `.setChoices must be called either with array of choices with a function resulting into Promise of array of choices`,
+      );
+    }
+
+    this.containerOuter.removeLoadingState();
+
+    this._startLoading();
+
+    type ChoiceGroup = {
+      id: string;
+      choices: Choice[];
+    };
+
+    choicesArrayOrFetcher.forEach((groupOrChoice: ChoiceGroup | Choice) => {
+      if ((groupOrChoice as ChoiceGroup).choices) {
+        this._addGroup({
+          id: groupOrChoice.id ? parseInt(`${groupOrChoice.id}`, 10) : null,
+          group: groupOrChoice,
+          valueKey: value,
+          labelKey: label,
+        });
+      } else {
+        const choice = groupOrChoice as Choice;
+        this._addChoice({
+          value: choice[value],
+          label: choice[label],
+          isSelected: !!choice.selected,
+          isDisabled: !!choice.disabled,
+          placeholder: !!choice.placeholder,
+          customProperties: choice.customProperties,
+        });
+      }
+    });
+
+    this._stopLoading();
+
+    return this;
+  }
+
+  clearChoices(): this {
+    this._store.dispatch(clearChoices());
+
+    return this;
+  }
+
+  clearStore(): this {
+    this._store.dispatch(clearAll());
+
+    return this;
+  }
+
+  clearInput(): this {
+    const shouldSetInputWidth = !this._isSelectOneElement;
+    this.input.clear(shouldSetInputWidth);
+
+    if (!this._isTextElement && this._canSearch) {
+      this._isSearching = false;
+      this._store.dispatch(activateChoices(true));
+    }
+
+    return this;
+  }
+
+  _render(): void {
+    if (this._store.isLoading()) {
+      return;
+    }
+
+    this._currentState = this._store.state;
+
+    const stateChanged =
+      this._currentState.choices !== this._prevState.choices ||
+      this._currentState.groups !== this._prevState.groups ||
+      this._currentState.items !== this._prevState.items;
+    const shouldRenderChoices = this._isSelectElement;
+    const shouldRenderItems =
+      this._currentState.items !== this._prevState.items;
+
+    if (!stateChanged) {
+      return;
+    }
+
+    if (shouldRenderChoices) {
+      this._renderChoices();
+    }
+
+    if (shouldRenderItems) {
+      this._renderItems();
+    }
+
+    this._prevState = this._currentState;
+  }
+
+  _renderChoices(): void {
+    const { activeGroups, activeChoices } = this._store;
+    let choiceListFragment = document.createDocumentFragment();
+
+    this.choiceList.clear();
+
+    if (this.config.resetScrollPosition) {
+      requestAnimationFrame(() => this.choiceList.scrollToTop());
+    }
+
+    // If we have grouped options
+    if (activeGroups.length >= 1 && !this._isSearching) {
+      // If we have a placeholder choice along with groups
+      const activePlaceholders = activeChoices.filter(
+        (activeChoice) =>
+          activeChoice.placeholder === true && activeChoice.groupId === -1,
+      );
+      if (activePlaceholders.length >= 1) {
+        choiceListFragment = this._createChoicesFragment(
+          activePlaceholders,
+          choiceListFragment,
+        );
+      }
+      choiceListFragment = this._createGroupsFragment(
+        activeGroups,
+        activeChoices,
+        choiceListFragment,
+      );
+    } else if (activeChoices.length >= 1) {
+      choiceListFragment = this._createChoicesFragment(
+        activeChoices,
+        choiceListFragment,
+      );
+    }
+
+    // If we have choices to show
+    if (
+      choiceListFragment.childNodes &&
+      choiceListFragment.childNodes.length > 0
+    ) {
+      const { activeItems } = this._store;
+      const canAddItem = this._canAddItem(activeItems, this.input.value);
+
+      // ...and we can select them
+      if (canAddItem.response) {
+        // ...append them and highlight the first choice
+        this.choiceList.append(choiceListFragment);
+        this._highlightChoice();
+      } else {
+        const notice = this._getTemplate('notice', canAddItem.notice);
+        this.choiceList.append(notice);
+      }
+    } else {
+      // Otherwise show a notice
+      let dropdownItem;
+      let notice;
+
+      if (this._isSearching) {
+        notice =
+          typeof this.config.noResultsText === 'function'
+            ? this.config.noResultsText()
+            : this.config.noResultsText;
+
+        dropdownItem = this._getTemplate('notice', notice, 'no-results');
+      } else {
+        notice =
+          typeof this.config.noChoicesText === 'function'
+            ? this.config.noChoicesText()
+            : this.config.noChoicesText;
+
+        dropdownItem = this._getTemplate('notice', notice, 'no-choices');
+      }
+
+      this.choiceList.append(dropdownItem);
+    }
+  }
+
+  _renderItems(): void {
+    const activeItems = this._store.activeItems || [];
+    this.itemList.clear();
+
+    // Create a fragment to store our list items
+    // (so we don't have to update the DOM for each item)
+    const itemListFragment = this._createItemsFragment(activeItems);
+
+    // If we have items to add, append them
+    if (itemListFragment.childNodes) {
+      this.itemList.append(itemListFragment);
+    }
+  }
+
+  _createGroupsFragment(
+    groups: Group[],
+    choices: Choice[],
+    fragment: DocumentFragment = document.createDocumentFragment(),
+  ): DocumentFragment {
+    const getGroupChoices = (group): Choice[] =>
+      choices.filter((choice) => {
+        if (this._isSelectOneElement) {
+          return choice.groupId === group.id;
+        }
+
+        return (
+          choice.groupId === group.id &&
+          (this.config.renderSelectedChoices === 'always' || !choice.selected)
+        );
+      });
+
+    // If sorting is enabled, filter groups
+    if (this.config.shouldSort) {
+      groups.sort(this.config.sorter);
+    }
+
+    groups.forEach((group) => {
+      const groupChoices = getGroupChoices(group);
+      if (groupChoices.length >= 1) {
+        const dropdownGroup = this._getTemplate('choiceGroup', group);
+        fragment.appendChild(dropdownGroup);
+        this._createChoicesFragment(groupChoices, fragment, true);
+      }
+    });
+
+    return fragment;
+  }
+
+  _createChoicesFragment(
+    choices: Choice[],
+    fragment: DocumentFragment = document.createDocumentFragment(),
+    withinGroup = false,
+  ): DocumentFragment {
+    // Create a fragment to store our list items (so we don't have to update the DOM for each item)
+    const { renderSelectedChoices, searchResultLimit, renderChoiceLimit } =
+      this.config;
+    const filter = this._isSearching ? sortByScore : this.config.sorter;
+    const appendChoice = (choice: Choice): void => {
+      const shouldRender =
+        renderSelectedChoices === 'auto'
+          ? this._isSelectOneElement || !choice.selected
+          : true;
+
+      if (shouldRender) {
+        const dropdownItem = this._getTemplate(
+          'choice',
+          choice,
+          this.config.itemSelectText,
+        );
+
+        fragment.appendChild(dropdownItem);
+      }
+    };
+
+    let rendererableChoices = choices;
+
+    if (renderSelectedChoices === 'auto' && !this._isSelectOneElement) {
+      rendererableChoices = choices.filter((choice) => !choice.selected);
+    }
+
+    // Split array into placeholders and "normal" choices
+    const { placeholderChoices, normalChoices } = rendererableChoices.reduce(
+      (acc, choice: Choice) => {
+        if (choice.placeholder) {
+          acc.placeholderChoices.push(choice);
+        } else {
+          acc.normalChoices.push(choice);
+        }
+
+        return acc;
+      },
+      {
+        placeholderChoices: [] as Choice[],
+        normalChoices: [] as Choice[],
+      },
+    );
+
+    // If sorting is enabled or the user is searching, filter choices
+    if (this.config.shouldSort || this._isSearching) {
+      normalChoices.sort(filter);
+    }
+
+    let choiceLimit = rendererableChoices.length;
+
+    // Prepend placeholeder
+    const sortedChoices = this._isSelectOneElement
+      ? [...placeholderChoices, ...normalChoices]
+      : normalChoices;
+
+    if (this._isSearching) {
+      choiceLimit = searchResultLimit;
+    } else if (renderChoiceLimit && renderChoiceLimit > 0 && !withinGroup) {
+      choiceLimit = renderChoiceLimit;
+    }
+
+    // Add each choice to dropdown within range
+    for (let i = 0; i < choiceLimit; i += 1) {
+      if (sortedChoices[i]) {
+        appendChoice(sortedChoices[i]);
+      }
+    }
+
+    return fragment;
+  }
+
+  _createItemsFragment(
+    items: Item[],
+    fragment: DocumentFragment = document.createDocumentFragment(),
+  ): DocumentFragment {
+    // Create fragment to add elements to
+    const { shouldSortItems, sorter, removeItemButton } = this.config;
+
+    // If sorting is enabled, filter items
+    if (shouldSortItems && !this._isSelectOneElement) {
+      items.sort(sorter);
+    }
+
+    if (this._isTextElement) {
+      // Update the value of the hidden input
+      this.passedElement.value = items
+        .map(({ value }) => value)
+        .join(this.config.delimiter);
+    } else {
+      // Update the options of the hidden input
+      (this.passedElement as WrappedSelect).options = items;
+    }
+
+    const addItemToFragment = (item: Item): void => {
+      // Create new list element
+      const listItem = this._getTemplate('item', item, removeItemButton);
+      // Append it to list
+      fragment.appendChild(listItem);
+    };
+
+    // Add each list item to list
+    items.forEach(addItemToFragment);
+
+    return fragment;
+  }
+
+  _triggerChange(value): void {
+    if (value === undefined || value === null) {
+      return;
+    }
+
+    this.passedElement.triggerEvent(EVENTS.change, {
+      value,
+    });
+  }
+
+  _selectPlaceholderChoice(placeholderChoice: Choice): void {
+    this._addItem({
+      value: placeholderChoice.value,
+      label: placeholderChoice.label,
+      choiceId: placeholderChoice.id,
+      groupId: placeholderChoice.groupId,
+      placeholder: placeholderChoice.placeholder,
+    });
+
+    this._triggerChange(placeholderChoice.value);
+  }
+
+  _handleButtonAction(activeItems?: Item[], element?: HTMLElement): void {
+    if (
+      !activeItems ||
+      !element ||
+      !this.config.removeItems ||
+      !this.config.removeItemButton
+    ) {
+      return;
+    }
+
+    const itemId =
+      element.parentNode && (element.parentNode as HTMLElement).dataset.id;
+    const itemToRemove =
+      itemId && activeItems.find((item) => item.id === parseInt(itemId, 10));
+
+    if (!itemToRemove) {
+      return;
+    }
+
+    // Remove item associated with button
+    this._removeItem(itemToRemove);
+    this._triggerChange(itemToRemove.value);
+
+    if (this._isSelectOneElement && this._store.placeholderChoice) {
+      this._selectPlaceholderChoice(this._store.placeholderChoice);
+    }
+  }
+
+  _handleItemAction(
+    activeItems?: Item[],
+    element?: HTMLElement,
+    hasShiftKey = false,
+  ): void {
+    if (
+      !activeItems ||
+      !element ||
+      !this.config.removeItems ||
+      this._isSelectOneElement
+    ) {
+      return;
+    }
+
+    const passedId = element.dataset.id;
+
+    // We only want to select one item with a click
+    // so we deselect any items that aren't the target
+    // unless shift is being pressed
+    activeItems.forEach((item) => {
+      if (item.id === parseInt(`${passedId}`, 10) && !item.highlighted) {
+        this.highlightItem(item);
+      } else if (!hasShiftKey && item.highlighted) {
+        this.unhighlightItem(item);
+      }
+    });
+
+    // Focus input as without focus, a user cannot do anything with a
+    // highlighted item
+    this.input.focus();
+  }
+
+  _handleChoiceAction(activeItems?: Item[], element?: HTMLElement): void {
+    if (!activeItems || !element) {
+      return;
+    }
+
+    // If we are clicking on an option
+    const { id } = element.dataset;
+    const choice = id && this._store.getChoiceById(id);
+    if (!choice) {
+      return;
+    }
+
+    const passedKeyCode =
+      activeItems[0] && activeItems[0].keyCode
+        ? activeItems[0].keyCode
+        : undefined;
+    const hasActiveDropdown = this.dropdown.isActive;
+
+    // Update choice keyCode
+    choice.keyCode = passedKeyCode;
+
+    this.passedElement.triggerEvent(EVENTS.choice, {
+      choice,
+    });
+
+    if (!choice.selected && !choice.disabled) {
+      const canAddItem = this._canAddItem(activeItems, choice.value);
+
+      if (canAddItem.response) {
+        this._addItem({
+          value: choice.value,
+          label: choice.label,
+          choiceId: choice.id,
+          groupId: choice.groupId,
+          customProperties: choice.customProperties,
+          placeholder: choice.placeholder,
+          keyCode: choice.keyCode,
+        });
+
+        this._triggerChange(choice.value);
+      }
+    }
+
+    this.clearInput();
+
+    // We want to close the dropdown if we are dealing with a single select box
+    if (hasActiveDropdown && this._isSelectOneElement) {
+      this.hideDropdown(true);
+      this.containerOuter.focus();
+    }
+  }
+
+  _handleBackspace(activeItems?: Item[]): void {
+    if (!this.config.removeItems || !activeItems) {
+      return;
+    }
+
+    const lastItem = activeItems[activeItems.length - 1];
+    const hasHighlightedItems = activeItems.some((item) => item.highlighted);
+
+    // If editing the last item is allowed and there are not other selected items,
+    // we can edit the item value. Otherwise if we can remove items, remove all selected items
+    if (this.config.editItems && !hasHighlightedItems && lastItem) {
+      this.input.value = lastItem.value;
+      this.input.setWidth();
+      this._removeItem(lastItem);
+      this._triggerChange(lastItem.value);
+    } else {
+      if (!hasHighlightedItems) {
+        // Highlight last item if none already highlighted
+        this.highlightItem(lastItem, false);
+      }
+      this.removeHighlightedItems(true);
+    }
+  }
+
+  _startLoading(): void {
+    this._store.dispatch(setIsLoading(true));
+  }
+
+  _stopLoading(): void {
+    this._store.dispatch(setIsLoading(false));
+  }
+
+  _handleLoadingState(setLoading = true): void {
+    let placeholderItem = this.itemList.getChild(
+      `.${this.config.classNames.placeholder}`,
+    );
+
+    if (setLoading) {
+      this.disable();
+      this.containerOuter.addLoadingState();
+
+      if (this._isSelectOneElement) {
+        if (!placeholderItem) {
+          placeholderItem = this._getTemplate(
+            'placeholder',
+            this.config.loadingText,
+          );
+
+          if (placeholderItem) {
+            this.itemList.append(placeholderItem);
+          }
+        } else {
+          placeholderItem.innerHTML = this.config.loadingText;
+        }
+      } else {
+        this.input.placeholder = this.config.loadingText;
+      }
+    } else {
+      this.enable();
+      this.containerOuter.removeLoadingState();
+
+      if (this._isSelectOneElement) {
+        if (placeholderItem) {
+          placeholderItem.innerHTML = this._placeholderValue || '';
+        }
+      } else {
+        this.input.placeholder = this._placeholderValue || '';
+      }
+    }
+  }
+
+  _handleSearch(value: string): void {
+    if (!this.input.isFocussed) {
+      return;
+    }
+
+    const { choices } = this._store;
+    const { searchFloor, searchChoices } = this.config;
+    const hasUnactiveChoices = choices.some((option) => !option.active);
+
+    // Check that we have a value to search and the input was an alphanumeric character
+    if (
+      value !== null &&
+      typeof value !== 'undefined' &&
+      value.length >= searchFloor
+    ) {
+      const resultCount = searchChoices ? this._searchChoices(value) : 0;
+      // Trigger search event
+      this.passedElement.triggerEvent(EVENTS.search, {
+        value,
+        resultCount,
+      });
+    } else if (hasUnactiveChoices) {
+      // Otherwise reset choices to active
+      this._isSearching = false;
+      this._store.dispatch(activateChoices(true));
+    }
+  }
+
+  _canAddItem(activeItems: Item[], value: string): Notice {
+    let canAddItem = true;
+    let notice =
+      typeof this.config.addItemText === 'function'
+        ? this.config.addItemText(value)
+        : this.config.addItemText;
+
+    if (!this._isSelectOneElement) {
+      const isDuplicateValue = existsInArray(activeItems, value);
+
+      if (
+        this.config.maxItemCount > 0 &&
+        this.config.maxItemCount <= activeItems.length
+      ) {
+        // If there is a max entry limit and we have reached that limit
+        // don't update
+        canAddItem = false;
+        notice =
+          typeof this.config.maxItemText === 'function'
+            ? this.config.maxItemText(this.config.maxItemCount)
+            : this.config.maxItemText;
+      }
+
+      if (
+        !this.config.duplicateItemsAllowed &&
+        isDuplicateValue &&
+        canAddItem
+      ) {
+        canAddItem = false;
+        notice =
+          typeof this.config.uniqueItemText === 'function'
+            ? this.config.uniqueItemText(value)
+            : this.config.uniqueItemText;
+      }
+
+      if (
+        this._isTextElement &&
+        this.config.addItems &&
+        canAddItem &&
+        typeof this.config.addItemFilter === 'function' &&
+        !this.config.addItemFilter(value)
+      ) {
+        canAddItem = false;
+        notice =
+          typeof this.config.customAddItemText === 'function'
+            ? this.config.customAddItemText(value)
+            : this.config.customAddItemText;
+      }
+    }
+
+    return {
+      response: canAddItem,
+      notice,
+    };
+  }
+
+  _searchChoices(value: string): number {
+    const newValue = typeof value === 'string' ? value.trim() : value;
+    const currentValue =
+      typeof this._currentValue === 'string'
+        ? this._currentValue.trim()
+        : this._currentValue;
+
+    if (newValue.length < 1 && newValue === `${currentValue} `) {
+      return 0;
+    }
+
+    // If new value matches the desired length and is not the same as the current value with a space
+    const haystack = this._store.searchableChoices;
+    const needle = newValue;
+    const options = Object.assign(this.config.fuseOptions, {
+      keys: [...this.config.searchFields],
+      includeMatches: true,
+    }) as Fuse.IFuseOptions<Choice>;
+    const fuse = new Fuse(haystack, options);
+    const results: Result<Choice>[] = fuse.search(needle) as any[]; // see https://github.com/krisk/Fuse/issues/303
+
+    this._currentValue = newValue;
+    this._highlightPosition = 0;
+    this._isSearching = true;
+    this._store.dispatch(filterChoices(results));
+
+    return results.length;
+  }
+
+  _addEventListeners(): void {
+    const { documentElement } = document;
+
+    // capture events - can cancel event processing or propagation
+    documentElement.addEventListener('touchend', this._onTouchEnd, true);
+    this.containerOuter.element.addEventListener(
+      'keydown',
+      this._onKeyDown,
+      true,
+    );
+    this.containerOuter.element.addEventListener(
+      'mousedown',
+      this._onMouseDown,
+      true,
+    );
+
+    // passive events - doesn't call `preventDefault` or `stopPropagation`
+    documentElement.addEventListener('click', this._onClick, { passive: true });
+    documentElement.addEventListener('touchmove', this._onTouchMove, {
+      passive: true,
+    });
+    this.dropdown.element.addEventListener('mouseover', this._onMouseOver, {
+      passive: true,
+    });
+
+    if (this._isSelectOneElement) {
+      this.containerOuter.element.addEventListener('focus', this._onFocus, {
+        passive: true,
+      });
+      this.containerOuter.element.addEventListener('blur', this._onBlur, {
+        passive: true,
+      });
+    }
+
+    this.input.element.addEventListener('keyup', this._onKeyUp, {
+      passive: true,
+    });
+
+    this.input.element.addEventListener('focus', this._onFocus, {
+      passive: true,
+    });
+    this.input.element.addEventListener('blur', this._onBlur, {
+      passive: true,
+    });
+
+    if (this.input.element.form) {
+      this.input.element.form.addEventListener('reset', this._onFormReset, {
+        passive: true,
+      });
+    }
+
+    this.input.addEventListeners();
+  }
+
+  _removeEventListeners(): void {
+    const { documentElement } = document;
+
+    documentElement.removeEventListener('touchend', this._onTouchEnd, true);
+    this.containerOuter.element.removeEventListener(
+      'keydown',
+      this._onKeyDown,
+      true,
+    );
+    this.containerOuter.element.removeEventListener(
+      'mousedown',
+      this._onMouseDown,
+      true,
+    );
+
+    documentElement.removeEventListener('click', this._onClick);
+    documentElement.removeEventListener('touchmove', this._onTouchMove);
+    this.dropdown.element.removeEventListener('mouseover', this._onMouseOver);
+
+    if (this._isSelectOneElement) {
+      this.containerOuter.element.removeEventListener('focus', this._onFocus);
+      this.containerOuter.element.removeEventListener('blur', this._onBlur);
+    }
+
+    this.input.element.removeEventListener('keyup', this._onKeyUp);
+    this.input.element.removeEventListener('focus', this._onFocus);
+    this.input.element.removeEventListener('blur', this._onBlur);
+
+    if (this.input.element.form) {
+      this.input.element.form.removeEventListener('reset', this._onFormReset);
+    }
+
+    this.input.removeEventListeners();
+  }
+
+  _onKeyDown(event: KeyboardEvent): void {
+    const { keyCode } = event;
+    const { activeItems } = this._store;
+    const hasFocusedInput = this.input.isFocussed;
+    const hasActiveDropdown = this.dropdown.isActive;
+    const hasItems = this.itemList.hasChildren();
+    const keyString = String.fromCharCode(keyCode);
+    // eslint-disable-next-line no-control-regex
+    const wasPrintableChar = /[^\x00-\x1F]/.test(keyString);
+
+    const {
+      BACK_KEY,
+      DELETE_KEY,
+      ENTER_KEY,
+      A_KEY,
+      ESC_KEY,
+      UP_KEY,
+      DOWN_KEY,
+      PAGE_UP_KEY,
+      PAGE_DOWN_KEY,
+    } = KEY_CODES;
+
+    if (!this._isTextElement && !hasActiveDropdown && wasPrintableChar) {
+      this.showDropdown();
+
+      if (!this.input.isFocussed) {
+        /*
+          We update the input value with the pressed key as
+          the input was not focussed at the time of key press
+          therefore does not have the value of the key.
+        */
+        this.input.value += event.key.toLowerCase();
+      }
+    }
+
+    switch (keyCode) {
+      case A_KEY:
+        return this._onSelectKey(event, hasItems);
+      case ENTER_KEY:
+        return this._onEnterKey(event, activeItems, hasActiveDropdown);
+      case ESC_KEY:
+        return this._onEscapeKey(hasActiveDropdown);
+      case UP_KEY:
+      case PAGE_UP_KEY:
+      case DOWN_KEY:
+      case PAGE_DOWN_KEY:
+        return this._onDirectionKey(event, hasActiveDropdown);
+      case DELETE_KEY:
+      case BACK_KEY:
+        return this._onDeleteKey(event, activeItems, hasFocusedInput);
+      default:
+    }
+  }
+
+  _onKeyUp({
+    target,
+    keyCode,
+  }: Pick<KeyboardEvent, 'target' | 'keyCode'>): void {
+    const { value } = this.input;
+    const { activeItems } = this._store;
+    const canAddItem = this._canAddItem(activeItems, value);
+    const { BACK_KEY: backKey, DELETE_KEY: deleteKey } = KEY_CODES;
+
+    // We are typing into a text input and have a value, we want to show a dropdown
+    // notice. Otherwise hide the dropdown
+    if (this._isTextElement) {
+      const canShowDropdownNotice = canAddItem.notice && value;
+
+      if (canShowDropdownNotice) {
+        const dropdownItem = this._getTemplate('notice', canAddItem.notice);
+        this.dropdown.element.innerHTML = dropdownItem.outerHTML;
+        this.showDropdown(true);
+      } else {
+        this.hideDropdown(true);
+      }
+    } else {
+      const wasRemovalKeyCode = keyCode === backKey || keyCode === deleteKey;
+      const userHasRemovedValue =
+        wasRemovalKeyCode && target && !(target as HTMLSelectElement).value;
+      const canReactivateChoices = !this._isTextElement && this._isSearching;
+      const canSearch = this._canSearch && canAddItem.response;
+
+      if (userHasRemovedValue && canReactivateChoices) {
+        this._isSearching = false;
+        this._store.dispatch(activateChoices(true));
+      } else if (canSearch) {
+        this._handleSearch(this.input.rawValue);
+      }
+    }
+
+    this._canSearch = this.config.searchEnabled;
+  }
+
+  _onSelectKey(event: KeyboardEvent, hasItems: boolean): void {
+    const { ctrlKey, metaKey } = event;
+    const hasCtrlDownKeyPressed = ctrlKey || metaKey;
+
+    // If CTRL + A or CMD + A have been pressed and there are items to select
+    if (hasCtrlDownKeyPressed && hasItems) {
+      this._canSearch = false;
+
+      const shouldHightlightAll =
+        this.config.removeItems &&
+        !this.input.value &&
+        this.input.element === document.activeElement;
+
+      if (shouldHightlightAll) {
+        this.highlightAll();
+      }
+    }
+  }
+
+  _onEnterKey(
+    event: KeyboardEvent,
+    activeItems: Item[],
+    hasActiveDropdown: boolean,
+  ): void {
+    const { target } = event;
+    const { ENTER_KEY: enterKey } = KEY_CODES;
+    const targetWasButton =
+      target && (target as HTMLElement).hasAttribute('data-button');
+
+    if (this._isTextElement && target && (target as HTMLInputElement).value) {
+      const { value } = this.input;
+      const canAddItem = this._canAddItem(activeItems, value);
+
+      if (canAddItem.response) {
+        this.hideDropdown(true);
+        this._addItem({ value });
+        this._triggerChange(value);
+        this.clearInput();
+      }
+    }
+
+    if (targetWasButton) {
+      this._handleButtonAction(activeItems, target as HTMLElement);
+      event.preventDefault();
+    }
+
+    if (hasActiveDropdown) {
+      const highlightedChoice = this.dropdown.getChild(
+        `.${this.config.classNames.highlightedState}`,
+      );
+
+      if (highlightedChoice) {
+        // add enter keyCode value
+        if (activeItems[0]) {
+          activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign
+        }
+        this._handleChoiceAction(activeItems, highlightedChoice);
+      }
+
+      event.preventDefault();
+    } else if (this._isSelectOneElement) {
+      this.showDropdown();
+      event.preventDefault();
+    }
+  }
+
+  _onEscapeKey(hasActiveDropdown: boolean): void {
+    if (hasActiveDropdown) {
+      this.hideDropdown(true);
+      this.containerOuter.focus();
+    }
+  }
+
+  _onDirectionKey(event: KeyboardEvent, hasActiveDropdown: boolean): void {
+    const { keyCode, metaKey } = event;
+    const {
+      DOWN_KEY: downKey,
+      PAGE_UP_KEY: pageUpKey,
+      PAGE_DOWN_KEY: pageDownKey,
+    } = KEY_CODES;
+
+    // If up or down key is pressed, traverse through options
+    if (hasActiveDropdown || this._isSelectOneElement) {
+      this.showDropdown();
+      this._canSearch = false;
+
+      const directionInt =
+        keyCode === downKey || keyCode === pageDownKey ? 1 : -1;
+      const skipKey =
+        metaKey || keyCode === pageDownKey || keyCode === pageUpKey;
+      const selectableChoiceIdentifier = '[data-choice-selectable]';
+
+      let nextEl;
+      if (skipKey) {
+        if (directionInt > 0) {
+          nextEl = this.dropdown.element.querySelector(
+            `${selectableChoiceIdentifier}:last-of-type`,
+          );
+        } else {
+          nextEl = this.dropdown.element.querySelector(
+            selectableChoiceIdentifier,
+          );
+        }
+      } else {
+        const currentEl = this.dropdown.element.querySelector(
+          `.${this.config.classNames.highlightedState}`,
+        );
+        if (currentEl) {
+          nextEl = getAdjacentEl(
+            currentEl,
+            selectableChoiceIdentifier,
+            directionInt,
+          );
+        } else {
+          nextEl = this.dropdown.element.querySelector(
+            selectableChoiceIdentifier,
+          );
+        }
+      }
+
+      if (nextEl) {
+        // We prevent default to stop the cursor moving
+        // when pressing the arrow
+        if (
+          !isScrolledIntoView(nextEl, this.choiceList.element, directionInt)
+        ) {
+          this.choiceList.scrollToChildElement(nextEl, directionInt);
+        }
+        this._highlightChoice(nextEl);
+      }
+
+      // Prevent default to maintain cursor position whilst
+      // traversing dropdown options
+      event.preventDefault();
+    }
+  }
+
+  _onDeleteKey(
+    event: KeyboardEvent,
+    activeItems: Item[],
+    hasFocusedInput: boolean,
+  ): void {
+    const { target } = event;
+    // If backspace or delete key is pressed and the input has no value
+    if (
+      !this._isSelectOneElement &&
+      !(target as HTMLInputElement).value &&
+      hasFocusedInput
+    ) {
+      this._handleBackspace(activeItems);
+      event.preventDefault();
+    }
+  }
+
+  _onTouchMove(): void {
+    if (this._wasTap) {
+      this._wasTap = false;
+    }
+  }
+
+  _onTouchEnd(event: TouchEvent): void {
+    const { target } = event || (event as TouchEvent).touches[0];
+    const touchWasWithinContainer =
+      this._wasTap && this.containerOuter.element.contains(target as Node);
+
+    if (touchWasWithinContainer) {
+      const containerWasExactTarget =
+        target === this.containerOuter.element ||
+        target === this.containerInner.element;
+
+      if (containerWasExactTarget) {
+        if (this._isTextElement) {
+          this.input.focus();
+        } else if (this._isSelectMultipleElement) {
+          this.showDropdown();
+        }
+      }
+
+      // Prevents focus event firing
+      event.stopPropagation();
+    }
+
+    this._wasTap = true;
+  }
+
+  /**
+   * Handles mousedown event in capture mode for containetOuter.element
+   */
+  _onMouseDown(event: MouseEvent): void {
+    const { target } = event;
+    if (!(target instanceof HTMLElement)) {
+      return;
+    }
+
+    // If we have our mouse down on the scrollbar and are on IE11...
+    if (IS_IE11 && this.choiceList.element.contains(target)) {
+      // check if click was on a scrollbar area
+      const firstChoice = this.choiceList.element
+        .firstElementChild as HTMLElement;
+
+      const isOnScrollbar =
+        this._direction === 'ltr'
+          ? event.offsetX >= firstChoice.offsetWidth
+          : event.offsetX < firstChoice.offsetLeft;
+      this._isScrollingOnIe = isOnScrollbar;
+    }
+
+    if (target === this.input.element) {
+      return;
+    }
+
+    const item = target.closest('[data-button],[data-item],[data-choice]');
+    if (item instanceof HTMLElement) {
+      const hasShiftKey = event.shiftKey;
+      const { activeItems } = this._store;
+      const { dataset } = item;
+
+      if ('button' in dataset) {
+        this._handleButtonAction(activeItems, item);
+      } else if ('item' in dataset) {
+        this._handleItemAction(activeItems, item, hasShiftKey);
+      } else if ('choice' in dataset) {
+        this._handleChoiceAction(activeItems, item);
+      }
+    }
+
+    event.preventDefault();
+  }
+
+  /**
+   * Handles mouseover event over this.dropdown
+   * @param {MouseEvent} event
+   */
+  _onMouseOver({ target }: Pick<MouseEvent, 'target'>): void {
+    if (target instanceof HTMLElement && 'choice' in target.dataset) {
+      this._highlightChoice(target);
+    }
+  }
+
+  _onClick({ target }: Pick<MouseEvent, 'target'>): void {
+    const clickWasWithinContainer = this.containerOuter.element.contains(
+      target as Node,
+    );
+
+    if (clickWasWithinContainer) {
+      if (!this.dropdown.isActive && !this.containerOuter.isDisabled) {
+        if (this._isTextElement) {
+          if (document.activeElement !== this.input.element) {
+            this.input.focus();
+          }
+        } else {
+          this.showDropdown();
+          this.containerOuter.focus();
+        }
+      } else if (
+        this._isSelectOneElement &&
+        target !== this.input.element &&
+        !this.dropdown.element.contains(target as Node)
+      ) {
+        this.hideDropdown();
+      }
+    } else {
+      const hasHighlightedItems = this._store.highlightedActiveItems.length > 0;
+
+      if (hasHighlightedItems) {
+        this.unhighlightAll();
+      }
+
+      this.containerOuter.removeFocusState();
+      this.hideDropdown(true);
+    }
+  }
+
+  _onFocus({ target }: Pick<FocusEvent, 'target'>): void {
+    const focusWasWithinContainer =
+      target && this.containerOuter.element.contains(target as Node);
+
+    if (!focusWasWithinContainer) {
+      return;
+    }
+
+    const focusActions = {
+      [TEXT_TYPE]: (): void => {
+        if (target === this.input.element) {
+          this.containerOuter.addFocusState();
+        }
+      },
+      [SELECT_ONE_TYPE]: (): void => {
+        this.containerOuter.addFocusState();
+        if (target === this.input.element) {
+          this.showDropdown(true);
+        }
+      },
+      [SELECT_MULTIPLE_TYPE]: (): void => {
+        if (target === this.input.element) {
+          this.showDropdown(true);
+          // If element is a select box, the focused element is the container and the dropdown
+          // isn't already open, focus and show dropdown
+          this.containerOuter.addFocusState();
+        }
+      },
+    };
+
+    focusActions[this.passedElement.element.type]();
+  }
+
+  _onBlur({ target }: Pick<FocusEvent, 'target'>): void {
+    const blurWasWithinContainer =
+      target && this.containerOuter.element.contains(target as Node);
+
+    if (blurWasWithinContainer && !this._isScrollingOnIe) {
+      const { activeItems } = this._store;
+      const hasHighlightedItems = activeItems.some((item) => item.highlighted);
+      const blurActions = {
+        [TEXT_TYPE]: (): void => {
+          if (target === this.input.element) {
+            this.containerOuter.removeFocusState();
+            if (hasHighlightedItems) {
+              this.unhighlightAll();
+            }
+            this.hideDropdown(true);
+          }
+        },
+        [SELECT_ONE_TYPE]: (): void => {
+          this.containerOuter.removeFocusState();
+          if (
+            target === this.input.element ||
+            (target === this.containerOuter.element && !this._canSearch)
+          ) {
+            this.hideDropdown(true);
+          }
+        },
+        [SELECT_MULTIPLE_TYPE]: (): void => {
+          if (target === this.input.element) {
+            this.containerOuter.removeFocusState();
+            this.hideDropdown(true);
+            if (hasHighlightedItems) {
+              this.unhighlightAll();
+            }
+          }
+        },
+      };
+
+      blurActions[this.passedElement.element.type]();
+    } else {
+      // On IE11, clicking the scollbar blurs our input and thus
+      // closes the dropdown. To stop this, we refocus our input
+      // if we know we are on IE *and* are scrolling.
+      this._isScrollingOnIe = false;
+      this.input.element.focus();
+    }
+  }
+
+  _onFormReset(): void {
+    this._store.dispatch(resetTo(this._initialState));
+  }
+
+  _highlightChoice(el: HTMLElement | null = null): void {
+    const choices: HTMLElement[] = Array.from(
+      this.dropdown.element.querySelectorAll('[data-choice-selectable]'),
+    );
+
+    if (!choices.length) {
+      return;
+    }
+
+    let passedEl = el;
+    const highlightedChoices = Array.from(
+      this.dropdown.element.querySelectorAll(
+        `.${this.config.classNames.highlightedState}`,
+      ),
+    );
+
+    // Remove any highlighted choices
+    highlightedChoices.forEach((choice) => {
+      choice.classList.remove(this.config.classNames.highlightedState);
+      choice.setAttribute('aria-selected', 'false');
+    });
+
+    if (passedEl) {
+      this._highlightPosition = choices.indexOf(passedEl);
+    } else {
+      // Highlight choice based on last known highlight location
+      if (choices.length > this._highlightPosition) {
+        // If we have an option to highlight
+        passedEl = choices[this._highlightPosition];
+      } else {
+        // Otherwise highlight the option before
+        passedEl = choices[choices.length - 1];
+      }
+
+      if (!passedEl) {
+        passedEl = choices[0];
+      }
+    }
+
+    passedEl.classList.add(this.config.classNames.highlightedState);
+    passedEl.setAttribute('aria-selected', 'true');
+    this.passedElement.triggerEvent(EVENTS.highlightChoice, { el: passedEl });
+
+    if (this.dropdown.isActive) {
+      // IE11 ignores aria-label and blocks virtual keyboard
+      // if aria-activedescendant is set without a dropdown
+      this.input.setActiveDescendant(passedEl.id);
+      this.containerOuter.setActiveDescendant(passedEl.id);
+    }
+  }
+
+  _addItem({
+    value,
+    label = null,
+    choiceId = -1,
+    groupId = -1,
+    customProperties = {},
+    placeholder = false,
+    keyCode = -1,
+  }: {
+    value: string;
+    label?: string | null;
+    choiceId?: number;
+    groupId?: number;
+    customProperties?: object;
+    placeholder?: boolean;
+    keyCode?: number;
+  }): void {
+    let passedValue = typeof value === 'string' ? value.trim() : value;
+
+    const { items } = this._store;
+    const passedLabel = label || passedValue;
+    const passedOptionId = choiceId || -1;
+    const group = groupId >= 0 ? this._store.getGroupById(groupId) : null;
+    const id = items ? items.length + 1 : 1;
+
+    // If a prepended value has been passed, prepend it
+    if (this.config.prependValue) {
+      passedValue = this.config.prependValue + passedValue.toString();
+    }
+
+    // If an appended value has been passed, append it
+    if (this.config.appendValue) {
+      passedValue += this.config.appendValue.toString();
+    }
+
+    this._store.dispatch(
+      addItem({
+        value: passedValue,
+        label: passedLabel,
+        id,
+        choiceId: passedOptionId,
+        groupId,
+        customProperties,
+        placeholder,
+        keyCode,
+      }),
+    );
+
+    if (this._isSelectOneElement) {
+      this.removeActiveItems(id);
+    }
+
+    // Trigger change event
+    this.passedElement.triggerEvent(EVENTS.addItem, {
+      id,
+      value: passedValue,
+      label: passedLabel,
+      customProperties,
+      groupValue: group && group.value ? group.value : null,
+      keyCode,
+    });
+  }
+
+  _removeItem(item: Item): void {
+    const { id, value, label, customProperties, choiceId, groupId } = item;
+    const group =
+      groupId && groupId >= 0 ? this._store.getGroupById(groupId) : null;
+
+    if (!id || !choiceId) {
+      return;
+    }
+
+    this._store.dispatch(removeItem(id, choiceId));
+    this.passedElement.triggerEvent(EVENTS.removeItem, {
+      id,
+      value,
+      label,
+      customProperties,
+      groupValue: group && group.value ? group.value : null,
+    });
+  }
+
+  _addChoice({
+    value,
+    label = null,
+    isSelected = false,
+    isDisabled = false,
+    groupId = -1,
+    customProperties = {},
+    placeholder = false,
+    keyCode = -1,
+  }: {
+    value: string;
+    label?: string | null;
+    isSelected?: boolean;
+    isDisabled?: boolean;
+    groupId?: number;
+    customProperties?: Record<string, any>;
+    placeholder?: boolean;
+    keyCode?: number;
+  }): void {
+    if (typeof value === 'undefined' || value === null) {
+      return;
+    }
+
+    // Generate unique id
+    const { choices } = this._store;
+    const choiceLabel = label || value;
+    const choiceId = choices ? choices.length + 1 : 1;
+    const choiceElementId = `${this._baseId}-${this._idNames.itemChoice}-${choiceId}`;
+
+    this._store.dispatch(
+      addChoice({
+        id: choiceId,
+        groupId,
+        elementId: choiceElementId,
+        value,
+        label: choiceLabel,
+        disabled: isDisabled,
+        customProperties,
+        placeholder,
+        keyCode,
+      }),
+    );
+
+    if (isSelected) {
+      this._addItem({
+        value,
+        label: choiceLabel,
+        choiceId,
+        customProperties,
+        placeholder,
+        keyCode,
+      });
+    }
+  }
+
+  _addGroup({ group, id, valueKey = 'value', labelKey = 'label' }): void {
+    const groupChoices: Choice[] | HTMLOptionElement[] = isType('Object', group)
+      ? group.choices
+      : Array.from(group.getElementsByTagName('OPTION'));
+    const groupId = id || Math.floor(new Date().valueOf() * Math.random());
+    const isDisabled = group.disabled ? group.disabled : false;
+
+    if (groupChoices) {
+      this._store.dispatch(
+        addGroup({
+          value: group.label,
+          id: groupId,
+          active: true,
+          disabled: isDisabled,
+        }),
+      );
+
+      const addGroupChoices = (choice: any): void => {
+        const isOptDisabled =
+          choice.disabled || (choice.parentNode && choice.parentNode.disabled);
+
+        this._addChoice({
+          value: choice[valueKey],
+          label: isType('Object', choice) ? choice[labelKey] : choice.innerHTML,
+          isSelected: choice.selected,
+          isDisabled: isOptDisabled,
+          groupId,
+          customProperties: choice.customProperties,
+          placeholder: choice.placeholder,
+        });
+      };
+
+      groupChoices.forEach(addGroupChoices);
+    } else {
+      this._store.dispatch(
+        addGroup({
+          value: group.label,
+          id: group.id,
+          active: false,
+          disabled: group.disabled,
+        }),
+      );
+    }
+  }
+
+  _getTemplate(template: string, ...args: any): any {
+    return this._templates[template].call(this, this.config, ...args);
+  }
+
+  _createTemplates(): void {
+    const { callbackOnCreateTemplates } = this.config;
+    let userTemplates = {};
+
+    if (
+      callbackOnCreateTemplates &&
+      typeof callbackOnCreateTemplates === 'function'
+    ) {
+      userTemplates = callbackOnCreateTemplates.call(this, strToEl);
+    }
+
+    this._templates = merge(templates, userTemplates);
+  }
+
+  _createElements(): void {
+    this.containerOuter = new Container({
+      element: this._getTemplate(
+        'containerOuter',
+        this._direction,
+        this._isSelectElement,
+        this._isSelectOneElement,
+        this.config.searchEnabled,
+        this.passedElement.element.type,
+        this.config.labelId,
+      ),
+      classNames: this.config.classNames,
+      type: this.passedElement.element.type as PassedElement['type'],
+      position: this.config.position,
+    });
+
+    this.containerInner = new Container({
+      element: this._getTemplate('containerInner'),
+      classNames: this.config.classNames,
+      type: this.passedElement.element.type as PassedElement['type'],
+      position: this.config.position,
+    });
+
+    this.input = new Input({
+      element: this._getTemplate('input', this._placeholderValue),
+      classNames: this.config.classNames,
+      type: this.passedElement.element.type as PassedElement['type'],
+      preventPaste: !this.config.paste,
+    });
+
+    this.choiceList = new List({
+      element: this._getTemplate('choiceList', this._isSelectOneElement),
+    });
+
+    this.itemList = new List({
+      element: this._getTemplate('itemList', this._isSelectOneElement),
+    });
+
+    this.dropdown = new Dropdown({
+      element: this._getTemplate('dropdown'),
+      classNames: this.config.classNames,
+      type: this.passedElement.element.type as PassedElement['type'],
+    });
+  }
+
+  _createStructure(): void {
+    // Hide original element
+    this.passedElement.conceal();
+    // Wrap input in container preserving DOM ordering
+    this.containerInner.wrap(this.passedElement.element);
+    // Wrapper inner container with outer container
+    this.containerOuter.wrap(this.containerInner.element);
+
+    if (this._isSelectOneElement) {
+      this.input.placeholder = this.config.searchPlaceholderValue || '';
+    } else if (this._placeholderValue) {
+      this.input.placeholder = this._placeholderValue;
+      this.input.setWidth();
+    }
+
+    this.containerOuter.element.appendChild(this.containerInner.element);
+    this.containerOuter.element.appendChild(this.dropdown.element);
+    this.containerInner.element.appendChild(this.itemList.element);
+
+    if (!this._isTextElement) {
+      this.dropdown.element.appendChild(this.choiceList.element);
+    }
+
+    if (!this._isSelectOneElement) {
+      this.containerInner.element.appendChild(this.input.element);
+    } else if (this.config.searchEnabled) {
+      this.dropdown.element.insertBefore(
+        this.input.element,
+        this.dropdown.element.firstChild,
+      );
+    }
+
+    if (this._isSelectElement) {
+      this._highlightPosition = 0;
+      this._isSearching = false;
+      this._startLoading();
+
+      if (this._presetGroups.length) {
+        this._addPredefinedGroups(this._presetGroups);
+      } else {
+        this._addPredefinedChoices(this._presetChoices);
+      }
+
+      this._stopLoading();
+    }
+
+    if (this._isTextElement) {
+      this._addPredefinedItems(this._presetItems);
+    }
+  }
+
+  _addPredefinedGroups(
+    groups: Group[] | HTMLOptGroupElement[] | Element[],
+  ): void {
+    // If we have a placeholder option
+    const placeholderChoice = (this.passedElement as WrappedSelect)
+      .placeholderOption;
+
+    if (
+      placeholderChoice &&
+      placeholderChoice.parentNode &&
+      (placeholderChoice.parentNode as HTMLElement).tagName === 'SELECT'
+    ) {
+      this._addChoice({
+        value: placeholderChoice.value,
+        label: placeholderChoice.innerHTML,
+        isSelected: placeholderChoice.selected,
+        isDisabled: placeholderChoice.disabled,
+        placeholder: true,
+      });
+    }
+
+    groups.forEach((group) =>
+      this._addGroup({
+        group,
+        id: group.id || null,
+      }),
+    );
+  }
+
+  _addPredefinedChoices(choices: Partial<Choice>[]): void {
+    // If sorting is enabled or the user is searching, filter choices
+    if (this.config.shouldSort) {
+      choices.sort(this.config.sorter);
+    }
+
+    const hasSelectedChoice = choices.some((choice) => choice.selected);
+    const firstEnabledChoiceIndex = choices.findIndex(
+      (choice) => choice.disabled === undefined || !choice.disabled,
+    );
+
+    choices.forEach((choice, index) => {
+      const { value = '', label, customProperties, placeholder } = choice;
+
+      if (this._isSelectElement) {
+        // If the choice is actually a group
+        if (choice.choices) {
+          this._addGroup({
+            group: choice,
+            id: choice.id || null,
+          });
+        } else {
+          /**
+           * If there is a selected choice already or the choice is not the first in
+           * the array, add each choice normally.
+           *
+           * Otherwise we pre-select the first enabled choice in the array ("select-one" only)
+           */
+          const shouldPreselect =
+            this._isSelectOneElement &&
+            !hasSelectedChoice &&
+            index === firstEnabledChoiceIndex;
+
+          const isSelected = shouldPreselect ? true : choice.selected;
+          const isDisabled = choice.disabled;
+
+          this._addChoice({
+            value,
+            label,
+            isSelected: !!isSelected,
+            isDisabled: !!isDisabled,
+            placeholder: !!placeholder,
+            customProperties,
+          });
+        }
+      } else {
+        this._addChoice({
+          value,
+          label,
+          isSelected: !!choice.selected,
+          isDisabled: !!choice.disabled,
+          placeholder: !!choice.placeholder,
+          customProperties,
+        });
+      }
+    });
+  }
+
+  _addPredefinedItems(items: Item[] | string[]): void {
+    items.forEach((item) => {
+      if (typeof item === 'object' && item.value) {
+        this._addItem({
+          value: item.value,
+          label: item.label,
+          choiceId: item.id,
+          customProperties: item.customProperties,
+          placeholder: item.placeholder,
+        });
+      }
+
+      if (typeof item === 'string') {
+        this._addItem({
+          value: item,
+        });
+      }
+    });
+  }
+
+  _setChoiceOrItem(item: any): void {
+    const itemType = getType(item).toLowerCase();
+    const handleType = {
+      object: (): void => {
+        if (!item.value) {
+          return;
+        }
+
+        // If we are dealing with a select input, we need to create an option first
+        // that is then selected. For text inputs we can just add items normally.
+        if (!this._isTextElement) {
+          this._addChoice({
+            value: item.value,
+            label: item.label,
+            isSelected: true,
+            isDisabled: false,
+            customProperties: item.customProperties,
+            placeholder: item.placeholder,
+          });
+        } else {
+          this._addItem({
+            value: item.value,
+            label: item.label,
+            choiceId: item.id,
+            customProperties: item.customProperties,
+            placeholder: item.placeholder,
+          });
+        }
+      },
+      string: (): void => {
+        if (!this._isTextElement) {
+          this._addChoice({
+            value: item,
+            label: item,
+            isSelected: true,
+            isDisabled: false,
+          });
+        } else {
+          this._addItem({
+            value: item,
+          });
+        }
+      },
+    };
+
+    handleType[itemType]();
+  }
+
+  _findAndSelectChoiceByValue(value: string): void {
+    const { choices } = this._store;
+    // Check 'value' property exists and the choice isn't already selected
+    const foundChoice = choices.find((choice) =>
+      this.config.valueComparer(choice.value, value),
+    );
+
+    if (foundChoice && !foundChoice.selected) {
+      this._addItem({
+        value: foundChoice.value,
+        label: foundChoice.label,
+        choiceId: foundChoice.id,
+        groupId: foundChoice.groupId,
+        customProperties: foundChoice.customProperties,
+        placeholder: foundChoice.placeholder,
+        keyCode: foundChoice.keyCode,
+      });
+    }
+  }
+
+  _generatePlaceholderValue(): string | null {
+    if (
+      this._isSelectElement &&
+      (this.passedElement as WrappedSelect).placeholderOption
+    ) {
+      const { placeholderOption } = this.passedElement as WrappedSelect;
+
+      return placeholderOption ? placeholderOption.text : null;
+    }
+
+    const { placeholder, placeholderValue } = this.config;
+    const {
+      element: { dataset },
+    } = this.passedElement;
+
+    if (placeholder) {
+      if (placeholderValue) {
+        return placeholderValue;
+      }
+
+      if (dataset.placeholder) {
+        return dataset.placeholder;
+      }
+    }
+
+    return null;
+  }
+}
+
+export default Choices;
diff --git a/my_flask_app/static/choices.js/src/scripts/components/container.test.ts b/my_flask_app/static/choices.js/src/scripts/components/container.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..790c773ff66824ded74b61a00077450138a9174a
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/container.test.ts
@@ -0,0 +1,485 @@
+import { expect } from 'chai';
+import { stub } from 'sinon';
+import { DEFAULT_CLASSNAMES } from '../defaults';
+import Container from './container';
+
+describe('components/container', () => {
+  let instance;
+  let element;
+
+  beforeEach(() => {
+    element = document.createElement('div');
+    element.id = 'container';
+
+    document.body.appendChild(element);
+    instance = new Container({
+      element: document.getElementById('container') as HTMLElement,
+      classNames: DEFAULT_CLASSNAMES,
+      position: 'auto',
+      type: 'text',
+    });
+  });
+
+  afterEach(() => {
+    document.body.innerHTML = '';
+    element = null;
+    instance = null;
+  });
+
+  describe('constructor', () => {
+    it('assigns choices element to class', () => {
+      expect(instance.element).to.eql(element);
+    });
+
+    it('assigns classnames to class', () => {
+      expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
+    });
+  });
+
+  describe('addEventListeners', () => {
+    let addEventListenerStub;
+
+    beforeEach(() => {
+      addEventListenerStub = stub(instance.element, 'addEventListener');
+    });
+
+    afterEach(() => {
+      addEventListenerStub.restore();
+    });
+
+    it('adds event listeners', () => {
+      instance.addEventListeners();
+      expect(addEventListenerStub.callCount).to.equal(2);
+      expect(addEventListenerStub.getCall(0).args[0]).to.equal('focus');
+      expect(addEventListenerStub.getCall(1).args[0]).to.equal('blur');
+    });
+  });
+
+  describe('removeEventListeners', () => {
+    let removeEventListenerStub;
+
+    beforeEach(() => {
+      removeEventListenerStub = stub(instance.element, 'removeEventListener');
+    });
+
+    afterEach(() => {
+      removeEventListenerStub.restore();
+    });
+
+    it('removes event listeners', () => {
+      instance.removeEventListeners();
+      expect(removeEventListenerStub.callCount).to.equal(2);
+      expect(removeEventListenerStub.getCall(0).args[0]).to.equal('focus');
+      expect(removeEventListenerStub.getCall(1).args[0]).to.equal('blur');
+    });
+  });
+
+  describe('onFocus', () => {
+    it('sets isFocussed flag to true', () => {
+      expect(instance.isFocussed).to.equal(false);
+      instance._onFocus();
+      expect(instance.isFocussed).to.equal(true);
+    });
+  });
+
+  describe('onBlur', () => {
+    it('sets isFocussed flag to false', () => {
+      instance.isFocussed = true;
+      instance._onBlur();
+      expect(instance.isFocussed).to.equal(false);
+    });
+  });
+
+  describe('shouldFlip', () => {
+    describe('not passing dropdownPos', () => {
+      it('returns false', () => {
+        expect(instance.shouldFlip()).to.equal(false);
+      });
+    });
+
+    describe('passing dropdownPos', () => {
+      describe('position config option set to "auto"', () => {
+        beforeEach(() => {
+          instance.position = 'auto';
+        });
+      });
+
+      describe('position config option set to "top"', () => {
+        beforeEach(() => {
+          instance.position = 'top';
+        });
+
+        it('returns true', () => {
+          expect(instance.shouldFlip(100)).to.equal(true);
+        });
+      });
+
+      describe('position config option set to "bottom"', () => {
+        beforeEach(() => {
+          instance.position = 'bottom';
+        });
+
+        it('returns false', () => {
+          expect(instance.shouldFlip(100)).to.equal(false);
+        });
+      });
+    });
+  });
+
+  describe('setActiveDescendant', () => {
+    it("sets element's aria-activedescendant attribute with passed descendant ID", () => {
+      const activeDescendantID = '1234';
+      expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
+        null,
+      );
+      instance.setActiveDescendant(activeDescendantID);
+      expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
+        activeDescendantID,
+      );
+    });
+  });
+
+  describe('removeActiveDescendant', () => {
+    it("remove elememnt's aria-activedescendant attribute", () => {
+      const activeDescendantID = '1234';
+      instance.element.setAttribute(
+        'aria-activedescendant',
+        activeDescendantID,
+      );
+      expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
+        activeDescendantID,
+      );
+      instance.removeActiveDescendant();
+      expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
+        null,
+      );
+    });
+  });
+
+  describe('open', () => {
+    beforeEach(() => {
+      instance.open();
+    });
+
+    it('adds open state class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.openState),
+      ).to.equal(true);
+    });
+
+    it('sets aria-expanded attribute to true', () => {
+      expect(instance.element.getAttribute('aria-expanded')).to.equal('true');
+    });
+
+    it('sets isOpen flag to true', () => {
+      expect(instance.isOpen).to.equal(true);
+    });
+
+    describe('flipping dropdown', () => {
+      let shouldFlipStub;
+      beforeEach(() => {
+        shouldFlipStub = stub().returns(true);
+
+        instance.shouldFlip = shouldFlipStub;
+        instance.open();
+      });
+
+      afterEach(() => {
+        instance.shouldFlip.reset();
+      });
+
+      it('adds adds flipped state class', () => {
+        expect(
+          instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState),
+        ).to.equal(true);
+      });
+
+      it('sets isFlipped flag to true', () => {
+        expect(instance.isFlipped).to.equal(true);
+      });
+    });
+  });
+
+  describe('close', () => {
+    beforeEach(() => {
+      instance.close();
+    });
+
+    it('adds open state class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.openState),
+      ).to.equal(false);
+    });
+
+    it('sets aria-expanded attribute to true', () => {
+      expect(instance.element.getAttribute('aria-expanded')).to.equal('false');
+    });
+
+    it('sets isOpen flag to true', () => {
+      expect(instance.isOpen).to.equal(false);
+    });
+
+    describe('flipped dropdown', () => {
+      beforeEach(() => {
+        instance.isFlipped = true;
+        instance.close();
+      });
+
+      it('removes adds flipped state class', () => {
+        expect(
+          instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState),
+        ).to.equal(false);
+      });
+
+      it('sets isFlipped flag to false', () => {
+        expect(instance.isFlipped).to.equal(false);
+      });
+    });
+  });
+
+  describe('focus', () => {
+    let focusStub;
+
+    beforeEach(() => {
+      focusStub = stub(instance.element, 'focus');
+    });
+
+    afterEach(() => {
+      focusStub.restore();
+    });
+
+    describe('isFocussed flag being set to false', () => {
+      it('focuses element', () => {
+        instance.isFocussed = false;
+        instance.focus();
+        expect(focusStub.called).to.equal(true);
+      });
+    });
+
+    describe('isFocussed flag being set to true', () => {
+      it('does not focus element', () => {
+        instance.isFocussed = true;
+        instance.focus();
+        expect(focusStub.called).to.equal(false);
+      });
+    });
+  });
+
+  describe('addFocusState', () => {
+    beforeEach(() => {
+      instance.removeLoadingState();
+    });
+
+    it('adds focus state class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState),
+      ).to.equal(false);
+      instance.addFocusState();
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState),
+      ).to.equal(true);
+    });
+  });
+
+  describe('removeFocusState', () => {
+    beforeEach(() => {
+      instance.addFocusState();
+    });
+
+    it('removes focus state class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState),
+      ).to.equal(true);
+      instance.removeFocusState();
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState),
+      ).to.equal(false);
+    });
+  });
+
+  describe('enable', () => {
+    beforeEach(() => {
+      instance.disable();
+    });
+
+    it('removes disabled state class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState),
+      ).to.equal(true);
+      instance.enable();
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState),
+      ).to.equal(false);
+    });
+
+    it('removes aria-disabled attribute', () => {
+      expect(instance.element.getAttribute('aria-disabled')).to.equal('true');
+      instance.enable();
+      expect(instance.element.getAttribute('aria-disabled')).to.equal(null);
+    });
+
+    it('sets isDisabled flag to true', () => {
+      instance.enable();
+      expect(instance.isDisabled).to.equal(false);
+    });
+
+    describe('select one element', () => {
+      beforeEach(() => {
+        instance.type = 'select-one';
+        instance.enable();
+      });
+
+      it('sets tabindex attribute', () => {
+        expect(instance.element.getAttribute('tabindex')).to.equal('0');
+      });
+    });
+  });
+
+  describe('disable', () => {
+    beforeEach(() => {
+      instance.enable();
+    });
+
+    it('removes disabled state class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState),
+      ).to.equal(false);
+      instance.disable();
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState),
+      ).to.equal(true);
+    });
+
+    it('removes aria-disabled attribute', () => {
+      expect(instance.element.getAttribute('aria-disabled')).to.equal(null);
+      instance.disable();
+      expect(instance.element.getAttribute('aria-disabled')).to.equal('true');
+    });
+
+    it('sets isDisabled flag to true', () => {
+      instance.disable();
+      expect(instance.isDisabled).to.equal(true);
+    });
+
+    describe('select one element', () => {
+      beforeEach(() => {
+        instance.type = 'select-one';
+        instance.disable();
+      });
+
+      it('sets tabindex attribute', () => {
+        expect(instance.element.getAttribute('tabindex')).to.equal('-1');
+      });
+    });
+  });
+
+  describe('wrap', () => {
+    let elementToWrap;
+
+    beforeEach(() => {
+      elementToWrap = document.createElement('div');
+      elementToWrap.id = 'wrap-test';
+      document.body.appendChild(elementToWrap);
+    });
+
+    afterEach(() => {
+      document.getElementById('wrap-test')!.remove();
+    });
+
+    it('wraps passed element inside element', () => {
+      expect(instance.element.querySelector('div#wrap-test')).to.equal(null);
+      instance.wrap(document.querySelector('div#wrap-test'));
+      expect(instance.element.querySelector('div#wrap-test')).to.equal(
+        elementToWrap,
+      );
+    });
+  });
+
+  describe('unwrap', () => {
+    let elementToUnwrap;
+
+    beforeEach(() => {
+      elementToUnwrap = document.createElement('div');
+      elementToUnwrap.id = 'unwrap-test';
+      document.body.appendChild(elementToUnwrap);
+      instance.wrap(document.getElementById('unwrap-test'));
+    });
+
+    afterEach(() => {
+      document.body.removeChild(document.getElementById('unwrap-test') as Node);
+    });
+
+    it('moves wrapped element outside of element', () => {
+      expect(
+        instance.element.querySelector('div#unwrap-test'),
+      ).to.be.instanceof(HTMLElement);
+      instance.unwrap(elementToUnwrap);
+      expect(instance.element.querySelector('div#unwrap-test')).to.equal(null);
+      expect(document.querySelector('div#unwrap-test')).to.be.instanceof(
+        HTMLElement,
+      );
+    });
+
+    it('removes element from DOM', () => {
+      expect(document.getElementById('container')).to.not.equal(null);
+      instance.unwrap(elementToUnwrap);
+      expect(document.getElementById('container')).to.equal(null);
+    });
+  });
+
+  describe('addLoadingState', () => {
+    beforeEach(() => {
+      instance.removeLoadingState();
+    });
+
+    it('adds loading state class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState),
+      ).to.equal(false);
+      instance.addLoadingState();
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState),
+      ).to.equal(true);
+    });
+
+    it('sets aria-busy attribute to true', () => {
+      expect(instance.element.getAttribute('aria-busy')).to.equal(null);
+      instance.addLoadingState();
+      expect(instance.element.getAttribute('aria-busy')).to.equal('true');
+    });
+
+    it('sets isLoading flag to false', () => {
+      expect(instance.isLoading).to.equal(false);
+      instance.addLoadingState();
+      expect(instance.isLoading).to.equal(true);
+    });
+  });
+
+  describe('removeLoadingState', () => {
+    beforeEach(() => {
+      instance.addLoadingState();
+    });
+
+    it('removes loading state class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState),
+      ).to.equal(true);
+      instance.removeLoadingState();
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState),
+      ).to.equal(false);
+    });
+
+    it('removes aria-busy attribute', () => {
+      expect(instance.element.getAttribute('aria-busy')).to.equal('true');
+      instance.removeLoadingState();
+      expect(instance.element.getAttribute('aria-busy')).to.equal(null);
+    });
+
+    it('sets isLoading flag to true', () => {
+      expect(instance.isLoading).to.equal(true);
+      instance.removeLoadingState();
+      expect(instance.isLoading).to.equal(false);
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/components/container.ts b/my_flask_app/static/choices.js/src/scripts/components/container.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b39c1a7857e697b375cce59000677910f8a5051
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/container.ts
@@ -0,0 +1,178 @@
+import { wrap } from '../lib/utils';
+import { SELECT_ONE_TYPE } from '../constants';
+import { ClassNames } from '../interfaces/class-names';
+import { PositionOptionsType } from '../interfaces/position-options-type';
+import { PassedElementType } from '../interfaces/passed-element-type';
+
+export default class Container {
+  element: HTMLElement;
+
+  type: PassedElementType;
+
+  classNames: ClassNames;
+
+  position: PositionOptionsType;
+
+  isOpen: boolean;
+
+  isFlipped: boolean;
+
+  isFocussed: boolean;
+
+  isDisabled: boolean;
+
+  isLoading: boolean;
+
+  constructor({
+    element,
+    type,
+    classNames,
+    position,
+  }: {
+    element: HTMLElement;
+    type: PassedElementType;
+    classNames: ClassNames;
+    position: PositionOptionsType;
+  }) {
+    this.element = element;
+    this.classNames = classNames;
+    this.type = type;
+    this.position = position;
+    this.isOpen = false;
+    this.isFlipped = false;
+    this.isFocussed = false;
+    this.isDisabled = false;
+    this.isLoading = false;
+    this._onFocus = this._onFocus.bind(this);
+    this._onBlur = this._onBlur.bind(this);
+  }
+
+  addEventListeners(): void {
+    this.element.addEventListener('focus', this._onFocus);
+    this.element.addEventListener('blur', this._onBlur);
+  }
+
+  removeEventListeners(): void {
+    this.element.removeEventListener('focus', this._onFocus);
+    this.element.removeEventListener('blur', this._onBlur);
+  }
+
+  /**
+   * Determine whether container should be flipped based on passed
+   * dropdown position
+   */
+  shouldFlip(dropdownPos: number): boolean {
+    if (typeof dropdownPos !== 'number') {
+      return false;
+    }
+
+    // If flip is enabled and the dropdown bottom position is
+    // greater than the window height flip the dropdown.
+    let shouldFlip = false;
+    if (this.position === 'auto') {
+      shouldFlip = !window.matchMedia(`(min-height: ${dropdownPos + 1}px)`)
+        .matches;
+    } else if (this.position === 'top') {
+      shouldFlip = true;
+    }
+
+    return shouldFlip;
+  }
+
+  setActiveDescendant(activeDescendantID: string): void {
+    this.element.setAttribute('aria-activedescendant', activeDescendantID);
+  }
+
+  removeActiveDescendant(): void {
+    this.element.removeAttribute('aria-activedescendant');
+  }
+
+  open(dropdownPos: number): void {
+    this.element.classList.add(this.classNames.openState);
+    this.element.setAttribute('aria-expanded', 'true');
+    this.isOpen = true;
+
+    if (this.shouldFlip(dropdownPos)) {
+      this.element.classList.add(this.classNames.flippedState);
+      this.isFlipped = true;
+    }
+  }
+
+  close(): void {
+    this.element.classList.remove(this.classNames.openState);
+    this.element.setAttribute('aria-expanded', 'false');
+    this.removeActiveDescendant();
+    this.isOpen = false;
+
+    // A dropdown flips if it does not have space within the page
+    if (this.isFlipped) {
+      this.element.classList.remove(this.classNames.flippedState);
+      this.isFlipped = false;
+    }
+  }
+
+  focus(): void {
+    if (!this.isFocussed) {
+      this.element.focus();
+    }
+  }
+
+  addFocusState(): void {
+    this.element.classList.add(this.classNames.focusState);
+  }
+
+  removeFocusState(): void {
+    this.element.classList.remove(this.classNames.focusState);
+  }
+
+  enable(): void {
+    this.element.classList.remove(this.classNames.disabledState);
+    this.element.removeAttribute('aria-disabled');
+    if (this.type === SELECT_ONE_TYPE) {
+      this.element.setAttribute('tabindex', '0');
+    }
+    this.isDisabled = false;
+  }
+
+  disable(): void {
+    this.element.classList.add(this.classNames.disabledState);
+    this.element.setAttribute('aria-disabled', 'true');
+    if (this.type === SELECT_ONE_TYPE) {
+      this.element.setAttribute('tabindex', '-1');
+    }
+    this.isDisabled = true;
+  }
+
+  wrap(element: HTMLSelectElement | HTMLInputElement | HTMLElement): void {
+    wrap(element, this.element);
+  }
+
+  unwrap(element: HTMLElement): void {
+    if (this.element.parentNode) {
+      // Move passed element outside this element
+      this.element.parentNode.insertBefore(element, this.element);
+      // Remove this element
+      this.element.parentNode.removeChild(this.element);
+    }
+  }
+
+  addLoadingState(): void {
+    this.element.classList.add(this.classNames.loadingState);
+    this.element.setAttribute('aria-busy', 'true');
+    this.isLoading = true;
+  }
+
+  removeLoadingState(): void {
+    this.element.classList.remove(this.classNames.loadingState);
+    this.element.removeAttribute('aria-busy');
+    this.isLoading = false;
+  }
+
+  _onFocus(): void {
+    this.isFocussed = true;
+  }
+
+  _onBlur(): void {
+    this.isFocussed = false;
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/components/dropdown.test.ts b/my_flask_app/static/choices.js/src/scripts/components/dropdown.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..25af3b41812c799cfba5e2be4349c2dc7b9c37bb
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/dropdown.test.ts
@@ -0,0 +1,143 @@
+import { expect } from 'chai';
+import sinon from 'sinon';
+import { DEFAULT_CLASSNAMES } from '../defaults';
+import Dropdown from './dropdown';
+
+describe('components/dropdown', () => {
+  let instance;
+  let choicesElement;
+
+  beforeEach(() => {
+    choicesElement = document.createElement('div');
+    document.body.appendChild(choicesElement);
+    instance = new Dropdown({
+      element: choicesElement,
+      type: 'text',
+      classNames: DEFAULT_CLASSNAMES,
+    });
+  });
+
+  afterEach(() => {
+    document.body.innerHTML = '';
+    instance = null;
+  });
+
+  describe('constructor', () => {
+    it('assigns choices element to instance', () => {
+      expect(instance.element).to.eql(choicesElement);
+    });
+
+    it('assigns classnames to instance', () => {
+      expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
+    });
+  });
+
+  describe('distanceFromTopWindow', () => {
+    let top;
+    let dimensions;
+    let getBoundingClientRectStub;
+
+    beforeEach(() => {
+      top = 100;
+      dimensions = {
+        bottom: 121,
+        height: 0,
+        left: 0,
+        right: 0,
+        top,
+        width: 0,
+      };
+
+      getBoundingClientRectStub = sinon
+        .stub(instance.element, 'getBoundingClientRect')
+        .returns(dimensions);
+    });
+
+    afterEach(() => {
+      getBoundingClientRectStub.restore();
+    });
+
+    it('determines how far the top of our element is from the top of the viewport', () => {
+      const expectedResponse = dimensions.bottom;
+      const actualResponse = instance.distanceFromTopWindow;
+      expect(actualResponse).to.equal(expectedResponse);
+    });
+  });
+
+  describe('getChild', () => {
+    let childElement;
+    const childClass = 'test-element';
+
+    beforeEach(() => {
+      childElement = document.createElement('span');
+      childElement.classList.add(childClass);
+      instance.element.appendChild(childElement);
+    });
+
+    it('returns child element', () => {
+      const expectedResponse = childElement;
+      const actualResponse = instance.getChild(`.${childClass}`);
+      expect(expectedResponse).to.eql(actualResponse);
+    });
+  });
+
+  describe('show', () => {
+    let actualResponse;
+
+    beforeEach(() => {
+      actualResponse = instance.show();
+    });
+
+    afterEach(() => {
+      instance.hide();
+    });
+
+    it('adds active class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.activeState),
+      ).to.equal(true);
+    });
+
+    it('sets expanded attribute', () => {
+      expect(instance.element.getAttribute('aria-expanded')).to.equal('true');
+    });
+
+    it('sets isActive instance flag', () => {
+      expect(instance.isActive).to.equal(true);
+    });
+
+    it('returns instance', () => {
+      expect(actualResponse).to.eql(instance);
+    });
+  });
+
+  describe('hide', () => {
+    let actualResponse;
+
+    beforeEach(() => {
+      actualResponse = instance.hide();
+    });
+
+    afterEach(() => {
+      instance.show();
+    });
+
+    it('adds active class', () => {
+      expect(
+        instance.element.classList.contains(DEFAULT_CLASSNAMES.activeState),
+      ).to.equal(false);
+    });
+
+    it('sets expanded attribute', () => {
+      expect(instance.element.getAttribute('aria-expanded')).to.equal('false');
+    });
+
+    it('sets isActive instance flag', () => {
+      expect(instance.isActive).to.equal(false);
+    });
+
+    it('returns instance', () => {
+      expect(actualResponse).to.eql(instance);
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/components/dropdown.ts b/my_flask_app/static/choices.js/src/scripts/components/dropdown.ts
new file mode 100644
index 0000000000000000000000000000000000000000..224d0da195ed477be04adfdaa1e5dd9b1588fde8
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/dropdown.ts
@@ -0,0 +1,60 @@
+import { ClassNames } from '../interfaces/class-names';
+import { PassedElementType } from '../interfaces/passed-element-type';
+
+export default class Dropdown {
+  element: HTMLElement;
+
+  type: PassedElementType;
+
+  classNames: ClassNames;
+
+  isActive: boolean;
+
+  constructor({
+    element,
+    type,
+    classNames,
+  }: {
+    element: HTMLElement;
+    type: PassedElementType;
+    classNames: ClassNames;
+  }) {
+    this.element = element;
+    this.classNames = classNames;
+    this.type = type;
+    this.isActive = false;
+  }
+
+  /**
+   * Bottom position of dropdown in viewport coordinates
+   */
+  get distanceFromTopWindow(): number {
+    return this.element.getBoundingClientRect().bottom;
+  }
+
+  getChild(selector: string): HTMLElement | null {
+    return this.element.querySelector(selector);
+  }
+
+  /**
+   * Show dropdown to user by adding active state class
+   */
+  show(): this {
+    this.element.classList.add(this.classNames.activeState);
+    this.element.setAttribute('aria-expanded', 'true');
+    this.isActive = true;
+
+    return this;
+  }
+
+  /**
+   * Hide dropdown from user
+   */
+  hide(): this {
+    this.element.classList.remove(this.classNames.activeState);
+    this.element.setAttribute('aria-expanded', 'false');
+    this.isActive = false;
+
+    return this;
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/components/index.ts b/my_flask_app/static/choices.js/src/scripts/components/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67d4e8543cd62698089ad6ec626b67fbbd57f28f
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/index.ts
@@ -0,0 +1,8 @@
+import Dropdown from './dropdown';
+import Container from './container';
+import Input from './input';
+import List from './list';
+import WrappedInput from './wrapped-input';
+import WrappedSelect from './wrapped-select';
+
+export { Dropdown, Container, Input, List, WrappedInput, WrappedSelect };
diff --git a/my_flask_app/static/choices.js/src/scripts/components/input.test.ts b/my_flask_app/static/choices.js/src/scripts/components/input.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..777f080a61bc6a3b70832671e1bdf6fb93ec2660
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/input.test.ts
@@ -0,0 +1,352 @@
+import { expect } from 'chai';
+import { stub } from 'sinon';
+import { DEFAULT_CLASSNAMES } from '../defaults';
+import Input from './input';
+
+describe('components/input', () => {
+  let instance;
+  let choicesElement;
+
+  beforeEach(() => {
+    choicesElement = document.createElement('input');
+    instance = new Input({
+      element: choicesElement,
+      type: 'text',
+      classNames: DEFAULT_CLASSNAMES,
+      preventPaste: false,
+    });
+  });
+
+  afterEach(() => {
+    document.body.innerHTML = '';
+    instance = null;
+  });
+
+  describe('constructor', () => {
+    it('assigns choices element to class', () => {
+      expect(instance.element).to.eql(choicesElement);
+    });
+
+    it('assigns classnames to class', () => {
+      expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
+    });
+  });
+
+  describe('addEventListeners', () => {
+    let addEventListenerStub;
+
+    beforeEach(() => {
+      addEventListenerStub = stub(instance.element, 'addEventListener');
+    });
+
+    afterEach(() => {
+      addEventListenerStub.restore();
+    });
+
+    it('adds event listeners', () => {
+      instance.addEventListeners();
+      expect(['input', 'paste', 'focus', 'blur']).to.have.members(
+        Array.from(
+          { length: addEventListenerStub.callCount },
+          (_, i) => addEventListenerStub.getCall(i).args[0],
+        ),
+      );
+    });
+  });
+
+  describe('removeEventListeners', () => {
+    let removeEventListenerStub;
+
+    beforeEach(() => {
+      removeEventListenerStub = stub(instance.element, 'removeEventListener');
+    });
+
+    afterEach(() => {
+      removeEventListenerStub.restore();
+    });
+
+    it('removes event listeners', () => {
+      instance.removeEventListeners();
+      expect(removeEventListenerStub.callCount).to.equal(4);
+      expect(removeEventListenerStub.getCall(0).args[0]).to.equal('input');
+      expect(removeEventListenerStub.getCall(1).args[0]).to.equal('paste');
+      expect(removeEventListenerStub.getCall(2).args[0]).to.equal('focus');
+      expect(removeEventListenerStub.getCall(3).args[0]).to.equal('blur');
+    });
+  });
+
+  describe('_onInput', () => {
+    let setWidthStub;
+
+    beforeEach(() => {
+      setWidthStub = stub(instance, 'setWidth');
+    });
+
+    afterEach(() => {
+      setWidthStub.restore();
+    });
+
+    describe('when element is select one', () => {
+      it('does not set input width', () => {
+        instance.type = 'select-one';
+        instance._onInput();
+        expect(setWidthStub.callCount).to.equal(0);
+      });
+    });
+
+    describe('when element is not a select one', () => {
+      it('sets input width', () => {
+        instance.type = 'text';
+        instance._onInput();
+        expect(setWidthStub.callCount).to.equal(1);
+      });
+    });
+  });
+
+  describe('_onPaste', () => {
+    let eventMock;
+
+    beforeEach(() => {
+      eventMock = {
+        preventDefault: stub(),
+        target: instance.element,
+      };
+    });
+
+    describe('when pasting is disabled and target is the element', () => {
+      it('prevents default pasting behaviour', () => {
+        instance.preventPaste = true;
+        instance._onPaste(eventMock);
+        expect(eventMock.preventDefault.callCount).to.equal(1);
+      });
+    });
+
+    describe('when pasting is enabled', () => {
+      it('does not prevent default pasting behaviour', () => {
+        instance.preventPaste = false;
+        instance._onPaste(eventMock);
+        expect(eventMock.preventDefault.callCount).to.equal(0);
+      });
+    });
+  });
+
+  describe('_onFocus', () => {
+    it('sets isFocussed flag to true', () => {
+      expect(instance.isFocussed).to.equal(false);
+      instance._onFocus();
+      expect(instance.isFocussed).to.equal(true);
+    });
+  });
+
+  describe('_onBlur', () => {
+    it('sets isFocussed flag to false', () => {
+      instance.isFocussed = true;
+      instance._onBlur();
+      expect(instance.isFocussed).to.equal(false);
+    });
+  });
+
+  describe('enable', () => {
+    beforeEach(() => {
+      instance.element.setAttribute('disabled', '');
+      instance.isDisabled = true;
+      instance.enable();
+    });
+
+    it('removes disabled attribute', () => {
+      expect(instance.element.getAttribute('disabled')).to.equal(null);
+    });
+
+    it('sets isDisabled flag to false', () => {
+      expect(instance.isDisabled).to.equal(false);
+    });
+  });
+
+  describe('disable', () => {
+    beforeEach(() => {
+      instance.element.removeAttribute('disabled', '');
+      instance.isDisabled = false;
+      instance.disable();
+    });
+
+    it('removes disabled attribute', () => {
+      expect(instance.element.getAttribute('disabled')).to.equal('');
+    });
+
+    it('sets isDisabled flag to false', () => {
+      expect(instance.isDisabled).to.equal(true);
+    });
+  });
+
+  describe('focus', () => {
+    let focusStub;
+
+    beforeEach(() => {
+      focusStub = stub(instance.element, 'focus');
+    });
+
+    afterEach(() => {
+      focusStub.restore();
+    });
+
+    describe('when element is not focussed', () => {
+      it('focuses element if isFocussed flag is set to false', () => {
+        instance.isFocussed = true;
+        instance.focus();
+        expect(focusStub.callCount).to.equal(0);
+      });
+    });
+
+    describe('when element is focussed', () => {
+      it('focuses element if isFocussed flag is set to false', () => {
+        instance.isFocussed = false;
+        instance.focus();
+        expect(focusStub.callCount).to.equal(1);
+      });
+    });
+  });
+
+  describe('blur', () => {
+    let blurStub;
+
+    beforeEach(() => {
+      blurStub = stub(instance.element, 'blur');
+    });
+
+    afterEach(() => {
+      blurStub.restore();
+    });
+
+    describe('when element is not focussed', () => {
+      it("doesn't blur element", () => {
+        instance.isFocussed = false;
+        instance.blur();
+        expect(blurStub.callCount).to.equal(0);
+      });
+    });
+
+    describe('when element is focussed', () => {
+      it('blurs element', () => {
+        instance.isFocussed = true;
+        instance.blur();
+        expect(blurStub.callCount).to.equal(1);
+      });
+    });
+  });
+
+  describe('clear', () => {
+    let setWidthStub;
+
+    beforeEach(() => {
+      setWidthStub = stub(instance, 'setWidth');
+    });
+
+    afterEach(() => {
+      setWidthStub.restore();
+    });
+
+    it("removes the element's value if it has one", () => {
+      instance.element.value = 'test';
+      expect(instance.element.value).to.equal('test');
+      instance.clear();
+      expect(instance.element.value).to.equal('');
+    });
+
+    it("sets the element's width if flag passed", () => {
+      expect(setWidthStub.callCount).to.equal(0);
+      instance.clear(true);
+      expect(setWidthStub.callCount).to.equal(1);
+    });
+
+    it('returns instance', () => {
+      const response = instance.clear();
+      expect(response).to.eql(instance);
+    });
+  });
+
+  /**
+   * Blocked by lack of ch support in JSDOM
+   * @see {@link https://github.com/jsdom/cssstyle/pull/107}
+   *
+  describe('setWidth', () => {
+    it('sets the width of the element based on input value and placeholder', () => {
+      instance.placeholder = 'This is a placeholder';
+      instance.element.value = 'This is a value';
+      expect(instance.element.style.width).to.not.equal('16ch');
+      instance.setWidth();
+      expect(instance.element.style.width).to.equal('16ch');
+      expect(instance.element.style.minWidth).to.equal('22ch');
+    });
+  });
+  */
+
+  describe('placeholder setter', () => {
+    it('sets value of element to passed placeholder', () => {
+      const placeholder = 'test';
+      expect(instance.element.placeholder).to.equal('');
+      instance.placeholder = placeholder;
+      expect(instance.element.placeholder).to.equal(placeholder);
+    });
+  });
+
+  describe('value setter', () => {
+    it('sets value of element to passed value', () => {
+      const value = 'test';
+      expect(instance.element.value).to.equal('');
+      instance.value = value;
+      expect(instance.element.value).to.equal(value);
+    });
+
+    it('casts value to string', () => {
+      const value = 1234;
+      instance.value = value;
+      expect(instance.element.value).to.equal(`${value}`);
+    });
+  });
+
+  describe('value getter', () => {
+    it('sets value of element to passed value', () => {
+      const value = 'test';
+      instance.element.value = value;
+      expect(instance.value).to.equal(value);
+    });
+
+    it('strips HTML from value', () => {
+      const value = '<script>somethingMalicious();</script>';
+      instance.element.value = value;
+      expect(instance.value).to.equal(
+        '&lt;script&gt;somethingMalicious();&lt;/script&gt;',
+      );
+    });
+  });
+
+  describe('setActiveDescendant', () => {
+    it("sets element's aria-activedescendant attribute with passed descendant ID", () => {
+      const activeDescendantID = '1234';
+      expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
+        null,
+      );
+      instance.setActiveDescendant(activeDescendantID);
+      expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
+        activeDescendantID,
+      );
+    });
+  });
+
+  describe('removeActiveDescendant', () => {
+    it("remove elememnt's aria-activedescendant attribute", () => {
+      const activeDescendantID = '1234';
+      instance.element.setAttribute(
+        'aria-activedescendant',
+        activeDescendantID,
+      );
+      expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
+        activeDescendantID,
+      );
+      instance.removeActiveDescendant();
+      expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
+        null,
+      );
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/components/input.ts b/my_flask_app/static/choices.js/src/scripts/components/input.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6eafc074afff8d3f71539ad464559b535c18df3b
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/input.ts
@@ -0,0 +1,151 @@
+import { sanitise } from '../lib/utils';
+import { SELECT_ONE_TYPE } from '../constants';
+import { ClassNames } from '../interfaces/class-names';
+import { PassedElementType } from '../interfaces/passed-element-type';
+
+export default class Input {
+  element: HTMLInputElement;
+
+  type: PassedElementType;
+
+  classNames: ClassNames;
+
+  preventPaste: boolean;
+
+  isFocussed: boolean;
+
+  isDisabled: boolean;
+
+  constructor({
+    element,
+    type,
+    classNames,
+    preventPaste,
+  }: {
+    element: HTMLInputElement;
+    type: PassedElementType;
+    classNames: ClassNames;
+    preventPaste: boolean;
+  }) {
+    this.element = element;
+    this.type = type;
+    this.classNames = classNames;
+    this.preventPaste = preventPaste;
+
+    this.isFocussed = this.element.isEqualNode(document.activeElement);
+    this.isDisabled = element.disabled;
+    this._onPaste = this._onPaste.bind(this);
+    this._onInput = this._onInput.bind(this);
+    this._onFocus = this._onFocus.bind(this);
+    this._onBlur = this._onBlur.bind(this);
+  }
+
+  set placeholder(placeholder: string) {
+    this.element.placeholder = placeholder;
+  }
+
+  get value(): string {
+    return sanitise(this.element.value);
+  }
+
+  set value(value: string) {
+    this.element.value = value;
+  }
+
+  get rawValue(): string {
+    return this.element.value;
+  }
+
+  addEventListeners(): void {
+    this.element.addEventListener('paste', this._onPaste);
+    this.element.addEventListener('input', this._onInput, {
+      passive: true,
+    });
+    this.element.addEventListener('focus', this._onFocus, {
+      passive: true,
+    });
+    this.element.addEventListener('blur', this._onBlur, {
+      passive: true,
+    });
+  }
+
+  removeEventListeners(): void {
+    this.element.removeEventListener('input', this._onInput);
+    this.element.removeEventListener('paste', this._onPaste);
+    this.element.removeEventListener('focus', this._onFocus);
+    this.element.removeEventListener('blur', this._onBlur);
+  }
+
+  enable(): void {
+    this.element.removeAttribute('disabled');
+    this.isDisabled = false;
+  }
+
+  disable(): void {
+    this.element.setAttribute('disabled', '');
+    this.isDisabled = true;
+  }
+
+  focus(): void {
+    if (!this.isFocussed) {
+      this.element.focus();
+    }
+  }
+
+  blur(): void {
+    if (this.isFocussed) {
+      this.element.blur();
+    }
+  }
+
+  clear(setWidth = true): this {
+    if (this.element.value) {
+      this.element.value = '';
+    }
+
+    if (setWidth) {
+      this.setWidth();
+    }
+
+    return this;
+  }
+
+  /**
+   * Set the correct input width based on placeholder
+   * value or input value
+   */
+  setWidth(): void {
+    // Resize input to contents or placeholder
+    const { style, value, placeholder } = this.element;
+    style.minWidth = `${placeholder.length + 1}ch`;
+    style.width = `${value.length + 1}ch`;
+  }
+
+  setActiveDescendant(activeDescendantID: string): void {
+    this.element.setAttribute('aria-activedescendant', activeDescendantID);
+  }
+
+  removeActiveDescendant(): void {
+    this.element.removeAttribute('aria-activedescendant');
+  }
+
+  _onInput(): void {
+    if (this.type !== SELECT_ONE_TYPE) {
+      this.setWidth();
+    }
+  }
+
+  _onPaste(event: ClipboardEvent): void {
+    if (this.preventPaste) {
+      event.preventDefault();
+    }
+  }
+
+  _onFocus(): void {
+    this.isFocussed = true;
+  }
+
+  _onBlur(): void {
+    this.isFocussed = false;
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/components/list.test.ts b/my_flask_app/static/choices.js/src/scripts/components/list.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c7b1b0ff40e373f7f84f214324f74f3db7a0577e
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/list.test.ts
@@ -0,0 +1,96 @@
+import { expect } from 'chai';
+import List from './list';
+
+describe('components/list', () => {
+  let instance;
+  let choicesElement;
+
+  beforeEach(() => {
+    choicesElement = document.createElement('div');
+    instance = new List({
+      element: choicesElement,
+    });
+  });
+
+  afterEach(() => {
+    document.body.innerHTML = '';
+    instance = null;
+  });
+
+  describe('constructor', () => {
+    it('assigns choices element to class', () => {
+      expect(instance.element).to.eql(choicesElement);
+    });
+
+    it('sets the height of the element', () => {
+      expect(instance.height).to.eql(choicesElement.scrollTop);
+    });
+  });
+
+  describe('clear', () => {
+    it("clears element's inner HTML", () => {
+      const innerHTML = 'test';
+      instance.element.innerHTML = innerHTML;
+      expect(instance.element.innerHTML).to.equal(innerHTML);
+      instance.clear();
+      expect(instance.element.innerHTML).to.equal('');
+    });
+  });
+
+  describe('append', () => {
+    it('appends passed node to element', () => {
+      const elementToAppend = document.createElement('span');
+      const childClass = 'test-element';
+      elementToAppend.classList.add(childClass);
+      expect(instance.element.querySelector(`.${childClass}`)).to.equal(null);
+      instance.append(elementToAppend);
+      expect(instance.element.querySelector(`.${childClass}`)).to.equal(
+        elementToAppend,
+      );
+    });
+  });
+
+  describe('getChild', () => {
+    let childElement;
+    const childClass = 'test-element';
+
+    beforeEach(() => {
+      childElement = document.createElement('span');
+      childElement.classList.add(childClass);
+      instance.element.appendChild(childElement);
+    });
+
+    it('returns child element', () => {
+      const expectedResponse = childElement;
+      const actualResponse = instance.getChild(`.${childClass}`);
+      expect(expectedResponse).to.eql(actualResponse);
+    });
+  });
+
+  describe('hasChildren', () => {
+    describe('when list has children', () => {
+      it('returns true', () => {
+        const childElement = document.createElement('span');
+        instance.element.appendChild(childElement);
+        const response = instance.hasChildren();
+        expect(response).to.equal(true);
+      });
+    });
+
+    describe('when list does not have children', () => {
+      it('returns false', () => {
+        instance.element.innerHTML = '';
+        const response = instance.hasChildren();
+        expect(response).to.equal(false);
+      });
+    });
+  });
+
+  describe('scrollToTop', () => {
+    it("sets the position's scroll position to 0", () => {
+      instance.element.scrollTop = 10;
+      instance.scrollToTop();
+      expect(instance.element.scrollTop).to.equal(0);
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/components/list.ts b/my_flask_app/static/choices.js/src/scripts/components/list.ts
new file mode 100644
index 0000000000000000000000000000000000000000..787d0f0ecdca813045f60f21366402fb1536b3f7
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/list.ts
@@ -0,0 +1,99 @@
+import { SCROLLING_SPEED } from '../constants';
+
+export default class List {
+  element: HTMLElement;
+
+  scrollPos: number;
+
+  height: number;
+
+  constructor({ element }: { element: HTMLElement }) {
+    this.element = element;
+    this.scrollPos = this.element.scrollTop;
+    this.height = this.element.offsetHeight;
+  }
+
+  clear(): void {
+    this.element.innerHTML = '';
+  }
+
+  append(node: Element | DocumentFragment): void {
+    this.element.appendChild(node);
+  }
+
+  getChild(selector: string): HTMLElement | null {
+    return this.element.querySelector(selector);
+  }
+
+  hasChildren(): boolean {
+    return this.element.hasChildNodes();
+  }
+
+  scrollToTop(): void {
+    this.element.scrollTop = 0;
+  }
+
+  scrollToChildElement(element: HTMLElement, direction: 1 | -1): void {
+    if (!element) {
+      return;
+    }
+
+    const listHeight = this.element.offsetHeight;
+    // Scroll position of dropdown
+    const listScrollPosition = this.element.scrollTop + listHeight;
+
+    const elementHeight = element.offsetHeight;
+    // Distance from bottom of element to top of parent
+    const elementPos = element.offsetTop + elementHeight;
+
+    // Difference between the element and scroll position
+    const destination =
+      direction > 0
+        ? this.element.scrollTop + elementPos - listScrollPosition
+        : element.offsetTop;
+
+    requestAnimationFrame(() => {
+      this._animateScroll(destination, direction);
+    });
+  }
+
+  _scrollDown(scrollPos: number, strength: number, destination: number): void {
+    const easing = (destination - scrollPos) / strength;
+    const distance = easing > 1 ? easing : 1;
+
+    this.element.scrollTop = scrollPos + distance;
+  }
+
+  _scrollUp(scrollPos: number, strength: number, destination: number): void {
+    const easing = (scrollPos - destination) / strength;
+    const distance = easing > 1 ? easing : 1;
+
+    this.element.scrollTop = scrollPos - distance;
+  }
+
+  _animateScroll(destination: number, direction: number): void {
+    const strength = SCROLLING_SPEED;
+    const choiceListScrollTop = this.element.scrollTop;
+    let continueAnimation = false;
+
+    if (direction > 0) {
+      this._scrollDown(choiceListScrollTop, strength, destination);
+
+      if (choiceListScrollTop < destination) {
+        continueAnimation = true;
+      }
+    } else {
+      this._scrollUp(choiceListScrollTop, strength, destination);
+
+      if (choiceListScrollTop > destination) {
+        continueAnimation = true;
+      }
+    }
+
+    if (continueAnimation) {
+      requestAnimationFrame(() => {
+        this._animateScroll(destination, direction);
+      });
+    }
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/components/wrapped-element.test.ts b/my_flask_app/static/choices.js/src/scripts/components/wrapped-element.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7d3e57642b2a1e0aef29601eb6feaa699fca540d
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/wrapped-element.test.ts
@@ -0,0 +1,179 @@
+import { expect } from 'chai';
+import { DEFAULT_CLASSNAMES } from '../defaults';
+import WrappedElement from './wrapped-element';
+
+describe('components/wrappedElement', () => {
+  let instance;
+  let element;
+
+  beforeEach(() => {
+    element = document.createElement('select');
+    instance = new WrappedElement({
+      element,
+      classNames: DEFAULT_CLASSNAMES,
+    });
+  });
+
+  afterEach(() => {
+    document.body.innerHTML = '';
+    instance = null;
+  });
+
+  describe('constructor', () => {
+    it('assigns choices element to class', () => {
+      expect(instance.element).to.eql(element);
+    });
+
+    it('assigns classnames to class', () => {
+      expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
+    });
+
+    it('sets isDisabled flag to false', () => {
+      expect(instance.isDisabled).to.eql(false);
+    });
+
+    describe('passing an element that is not an instance of HTMLInputElement or HTMLSelectElement', () => {
+      it('throws a TypeError', () => {
+        element = document.createElement('div');
+        expect(
+          () =>
+            new WrappedElement({
+              element,
+              classNames: DEFAULT_CLASSNAMES,
+            }),
+        ).to.throw(TypeError, 'Invalid element passed');
+      });
+    });
+  });
+
+  describe('value getter', () => {
+    it('returns element value', () => {
+      expect(instance.value).to.eql(element.value);
+    });
+  });
+
+  describe('isActive getter', () => {
+    it('returns whether the "data-choice" attribute is set to "active"', () => {
+      instance.element.dataset.choice = 'active';
+      expect(instance.isActive).to.equal(true);
+
+      instance.element.dataset.choice = 'inactive';
+      expect(instance.isActive).to.equal(false);
+    });
+  });
+
+  describe('dir getter', () => {
+    it('returns the direction of the element', () => {
+      expect(instance.dir).to.equal(instance.element.dir);
+    });
+  });
+
+  describe('conceal', () => {
+    let originalStyling;
+
+    beforeEach(() => {
+      originalStyling = 'color:red';
+      instance.element.setAttribute('style', originalStyling);
+    });
+
+    it('hides element', () => {
+      instance.conceal();
+      expect(instance.element.tabIndex).to.equal(-1);
+      expect(
+        instance.element.classList.contains(instance.classNames.input),
+      ).to.equal(true);
+      expect(instance.element.hidden).to.be.true;
+      expect(instance.element.getAttribute('data-choice')).to.equal('active');
+      expect(instance.element.getAttribute('data-choice-orig-style')).to.equal(
+        originalStyling,
+      );
+    });
+  });
+
+  describe('reveal', () => {
+    let originalStyling;
+
+    beforeEach(() => {
+      originalStyling = 'color:red';
+      instance.element.setAttribute('data-choice-orig-style', originalStyling);
+    });
+
+    it('shows element', () => {
+      instance.reveal();
+      expect(instance.element.tabIndex).to.equal(0);
+      expect(
+        instance.element.classList.contains(instance.classNames.input),
+      ).to.equal(false);
+      expect(instance.element.hidden).to.be.false;
+      expect(instance.element.getAttribute('style')).to.equal(originalStyling);
+      expect(instance.element.getAttribute('aria-hidden')).to.equal(null);
+      expect(instance.element.getAttribute('data-choice')).to.equal(null);
+      expect(instance.element.getAttribute('data-choice-orig-style')).to.equal(
+        null,
+      );
+    });
+  });
+
+  describe('enable', () => {
+    beforeEach(() => {
+      instance.disable();
+    });
+
+    it('removes disabled attribute', () => {
+      expect(instance.element.hasAttribute('disabled')).to.equal(true);
+      instance.enable();
+      expect(instance.element.hasAttribute('disabled')).to.equal(false);
+    });
+
+    it('sets elements disabled state to false', () => {
+      expect(instance.element.disabled).to.equal(true);
+      instance.enable();
+      expect(instance.element.disabled).to.equal(false);
+    });
+
+    it('sets isDisabled flag to false', () => {
+      expect(instance.isDisabled).to.equal(true);
+      instance.enable();
+      expect(instance.isDisabled).to.equal(false);
+    });
+  });
+
+  describe('disable', () => {
+    beforeEach(() => {
+      instance.enable();
+    });
+
+    it('sets disabled attribute (to blank string)', () => {
+      expect(instance.element.hasAttribute('disabled')).to.equal(false);
+      instance.disable();
+      expect(instance.element.getAttribute('disabled')).to.equal('');
+    });
+
+    it('sets elements disabled state to true', () => {
+      expect(instance.element.disabled).to.equal(false);
+      instance.disable();
+      expect(instance.element.disabled).to.equal(true);
+    });
+
+    it('sets isDisabled flag to true', () => {
+      expect(instance.isDisabled).to.equal(false);
+      instance.disable();
+      expect(instance.isDisabled).to.equal(true);
+    });
+  });
+
+  describe('triggerEvent', () => {
+    it('fires event on element using passed eventType and data', (done) => {
+      const data = {
+        test: true,
+      };
+
+      instance.element.addEventListener('testEvent', ({ detail }) => {
+        expect(detail).to.eql(data);
+        done();
+      });
+
+      instance.triggerEvent('testEvent', data);
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/components/wrapped-element.ts b/my_flask_app/static/choices.js/src/scripts/components/wrapped-element.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5d9d2eed84adefd78db086e404f66bbbdf714dbc
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/wrapped-element.ts
@@ -0,0 +1,98 @@
+import { ClassNames } from '../interfaces/class-names';
+import { EventType } from '../interfaces/event-type';
+import { dispatchEvent } from '../lib/utils';
+
+export default class WrappedElement {
+  element: HTMLInputElement | HTMLSelectElement;
+
+  classNames: ClassNames;
+
+  isDisabled: boolean;
+
+  constructor({ element, classNames }) {
+    this.element = element;
+    this.classNames = classNames;
+
+    if (
+      !(element instanceof HTMLInputElement) &&
+      !(element instanceof HTMLSelectElement)
+    ) {
+      throw new TypeError('Invalid element passed');
+    }
+
+    this.isDisabled = false;
+  }
+
+  get isActive(): boolean {
+    return this.element.dataset.choice === 'active';
+  }
+
+  get dir(): string {
+    return this.element.dir;
+  }
+
+  get value(): string {
+    return this.element.value;
+  }
+
+  set value(value: string) {
+    // you must define setter here otherwise it will be readonly property
+    this.element.value = value;
+  }
+
+  conceal(): void {
+    // Hide passed input
+    this.element.classList.add(this.classNames.input);
+    this.element.hidden = true;
+
+    // Remove element from tab index
+    this.element.tabIndex = -1;
+
+    // Backup original styles if any
+    const origStyle = this.element.getAttribute('style');
+
+    if (origStyle) {
+      this.element.setAttribute('data-choice-orig-style', origStyle);
+    }
+
+    this.element.setAttribute('data-choice', 'active');
+  }
+
+  reveal(): void {
+    // Reinstate passed element
+    this.element.classList.remove(this.classNames.input);
+    this.element.hidden = false;
+    this.element.removeAttribute('tabindex');
+
+    // Recover original styles if any
+    const origStyle = this.element.getAttribute('data-choice-orig-style');
+
+    if (origStyle) {
+      this.element.removeAttribute('data-choice-orig-style');
+      this.element.setAttribute('style', origStyle);
+    } else {
+      this.element.removeAttribute('style');
+    }
+    this.element.removeAttribute('data-choice');
+
+    // Re-assign values - this is weird, I know
+    // @todo Figure out why we need to do this
+    this.element.value = this.element.value; // eslint-disable-line no-self-assign
+  }
+
+  enable(): void {
+    this.element.removeAttribute('disabled');
+    this.element.disabled = false;
+    this.isDisabled = false;
+  }
+
+  disable(): void {
+    this.element.setAttribute('disabled', '');
+    this.element.disabled = true;
+    this.isDisabled = true;
+  }
+
+  triggerEvent(eventType: EventType, data?: object): void {
+    dispatchEvent(this.element, eventType, data);
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/components/wrapped-input.test.ts b/my_flask_app/static/choices.js/src/scripts/components/wrapped-input.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9aee451d89e9b47cbe0bfd7b9c0b44f49fa311d7
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/wrapped-input.test.ts
@@ -0,0 +1,66 @@
+import { expect } from 'chai';
+import { stub } from 'sinon';
+import { DEFAULT_CLASSNAMES } from '../defaults';
+import WrappedElement from './wrapped-element';
+import WrappedInput from './wrapped-input';
+
+describe('components/wrappedInput', () => {
+  let instance;
+  let element;
+  const delimiter = '-';
+
+  beforeEach(() => {
+    element = document.createElement('input');
+    instance = new WrappedInput({
+      element,
+      classNames: DEFAULT_CLASSNAMES,
+      delimiter,
+    });
+  });
+
+  afterEach(() => {
+    document.body.innerHTML = '';
+    instance = null;
+  });
+
+  describe('constructor', () => {
+    it('assigns choices element to class', () => {
+      expect(instance.element).to.eql(element);
+    });
+
+    it('assigns classnames to class', () => {
+      expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
+    });
+  });
+
+  describe('inherited methods', () => {
+    const methods: string[] = ['conceal', 'reveal', 'enable', 'disable'];
+
+    methods.forEach((method) => {
+      describe(method, () => {
+        beforeEach(() => {
+          stub(WrappedElement.prototype, method as keyof WrappedElement);
+        });
+
+        afterEach(() => {
+          WrappedElement.prototype[method].restore();
+        });
+
+        it(`calls super.${method}`, () => {
+          expect(WrappedElement.prototype[method].called).to.equal(false);
+          instance[method]();
+          expect(WrappedElement.prototype[method].called).to.equal(true);
+        });
+      });
+    });
+  });
+
+  describe('value setter', () => {
+    it('sets the value of the input to the given value', () => {
+      const newValue = 'Value 1, Value 2, Value 3';
+      expect(instance.element.value).to.equal('');
+      instance.value = newValue;
+      expect(instance.value).to.equal(newValue);
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/components/wrapped-input.ts b/my_flask_app/static/choices.js/src/scripts/components/wrapped-input.ts
new file mode 100644
index 0000000000000000000000000000000000000000..14bcf67b46d64f2396e4bf4ee943a44dad240760
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/wrapped-input.ts
@@ -0,0 +1,30 @@
+import { ClassNames } from '../interfaces/class-names';
+import WrappedElement from './wrapped-element';
+
+export default class WrappedInput extends WrappedElement {
+  element: HTMLInputElement;
+
+  delimiter: string;
+
+  constructor({
+    element,
+    classNames,
+    delimiter,
+  }: {
+    element: HTMLInputElement;
+    classNames: ClassNames;
+    delimiter: string;
+  }) {
+    super({ element, classNames });
+    this.delimiter = delimiter;
+  }
+
+  get value(): string {
+    return this.element.value;
+  }
+
+  set value(value: string) {
+    this.element.setAttribute('value', value);
+    this.element.value = value;
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/components/wrapped-select.test.ts b/my_flask_app/static/choices.js/src/scripts/components/wrapped-select.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..642e56691965fa41d1b5ce6f08625e4dd1876406
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/wrapped-select.test.ts
@@ -0,0 +1,183 @@
+import { expect } from 'chai';
+import { stub, spy } from 'sinon';
+import WrappedElement from './wrapped-element';
+import WrappedSelect from './wrapped-select';
+import Templates from '../templates';
+import { DEFAULT_CLASSNAMES } from '../defaults';
+
+describe('components/wrappedSelect', () => {
+  let instance;
+  let element;
+
+  beforeEach(() => {
+    element = document.createElement('select');
+    element.id = 'target';
+    for (let i = 0; i <= 4; i++) {
+      const option = document.createElement('option');
+
+      if (i === 0) {
+        option.value = '';
+        option.innerHTML = 'Placeholder label';
+      } else {
+        option.value = `Value ${i}`;
+        option.innerHTML = `Label ${i}`;
+      }
+
+      if (i === 1) {
+        option.setAttribute('placeholder', '');
+      }
+
+      element.appendChild(option);
+    }
+    document.body.appendChild(element);
+
+    instance = new WrappedSelect({
+      element: document.getElementById('target') as HTMLSelectElement,
+      classNames: DEFAULT_CLASSNAMES,
+      template: spy(Templates.option),
+    });
+  });
+
+  afterEach(() => {
+    document.body.innerHTML = '';
+    instance = null;
+  });
+
+  describe('constructor', () => {
+    it('assigns choices element to class', () => {
+      expect(instance.element).to.eql(element);
+    });
+
+    it('assigns classnames to class', () => {
+      expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
+    });
+  });
+
+  describe('inherited methods', () => {
+    const methods: string[] = ['conceal', 'reveal', 'enable', 'disable'];
+
+    methods.forEach((method) => {
+      beforeEach(() => {
+        stub(WrappedElement.prototype, method as keyof WrappedElement);
+      });
+
+      afterEach(() => {
+        WrappedElement.prototype[method].restore();
+      });
+
+      describe(method, () => {
+        it(`calls super.${method}`, () => {
+          expect(WrappedElement.prototype[method].called).to.equal(false);
+          instance[method]();
+          expect(WrappedElement.prototype[method].called).to.equal(true);
+        });
+      });
+    });
+  });
+
+  describe('placeholderOption getter', () => {
+    it('returns option element with empty value attribute', () => {
+      expect(instance.placeholderOption).to.be.instanceOf(HTMLOptionElement);
+      expect(instance.placeholderOption.value).to.equal('');
+    });
+
+    it('returns option element with placeholder attribute as fallback', () => {
+      instance.element.removeChild(instance.element.firstChild);
+
+      expect(instance.placeholderOption).to.be.instanceOf(HTMLOptionElement);
+      expect(instance.placeholderOption.value).to.equal('Value 1');
+    });
+  });
+
+  describe('options getter', () => {
+    it('returns all option elements', () => {
+      const { options } = instance;
+      expect(options).to.be.an('array');
+      options.forEach((option) => {
+        expect(option).to.be.instanceOf(HTMLOptionElement);
+      });
+    });
+  });
+
+  describe('optionGroups getter', () => {
+    it('returns an array of all option groups', () => {
+      for (let i = 1; i <= 3; i++) {
+        const group = document.createElement('optgroup');
+        instance.element.appendChild(group);
+      }
+
+      const { optionGroups } = instance;
+      expect(optionGroups.length).to.equal(3);
+      optionGroups.forEach((option) => {
+        expect(option).to.be.instanceOf(HTMLOptGroupElement);
+      });
+    });
+  });
+
+  describe('options setter', () => {
+    let appendDocFragmentStub;
+    const options = [
+      {
+        value: '1',
+        label: 'Test 1',
+        selected: false,
+        disabled: true,
+      },
+      {
+        value: '2',
+        label: 'Test 2',
+        selected: true,
+        disabled: false,
+      },
+    ];
+
+    beforeEach(() => {
+      appendDocFragmentStub = stub();
+      instance.appendDocFragment = appendDocFragmentStub;
+    });
+
+    afterEach(() => {
+      instance.appendDocFragment.reset();
+    });
+
+    it('creates an option element for each passed object, adds it to a fragment and calls appendDocFragment with created fragment', () => {
+      expect(appendDocFragmentStub.called).to.equal(false);
+      instance.options = options;
+      expect(appendDocFragmentStub.called).to.equal(true);
+
+      const fragment = appendDocFragmentStub.firstCall.args[0];
+      const selectElement = document.createElement('select');
+      selectElement.appendChild(fragment);
+
+      expect(fragment).to.be.instanceOf(DocumentFragment);
+      expect(instance.template.callCount).to.equal(2);
+      expect(selectElement.options.length).to.equal(2);
+      expect(selectElement.options[0].value).to.equal(options[0].value);
+      expect(selectElement.options[1].value).to.equal(options[1].value);
+    });
+  });
+
+  describe('appendDocFragment', () => {
+    it('empties contents of element', () => {
+      expect(instance.element.getElementsByTagName('option').length).to.equal(
+        5,
+      );
+      instance.appendDocFragment(document.createDocumentFragment());
+      expect(instance.element.getElementsByTagName('option').length).to.equal(
+        0,
+      );
+    });
+
+    it('appends passed fragment to element', () => {
+      const fragment = document.createDocumentFragment();
+      const elementToAppend = document.createElement('div');
+      elementToAppend.id = 'fragment-target';
+      fragment.appendChild(elementToAppend);
+      expect(instance.element.querySelector('#fragment-target')).to.equal(null);
+      instance.appendDocFragment(fragment);
+      expect(instance.element.querySelector('#fragment-target')).to.eql(
+        elementToAppend,
+      );
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/components/wrapped-select.ts b/my_flask_app/static/choices.js/src/scripts/components/wrapped-select.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3b2ada6c645a99b2dd4ac1b5a663d89b42a01f6d
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/components/wrapped-select.ts
@@ -0,0 +1,60 @@
+import { ClassNames } from '../interfaces/class-names';
+import { Item } from '../interfaces/item';
+import WrappedElement from './wrapped-element';
+
+export default class WrappedSelect extends WrappedElement {
+  element: HTMLSelectElement;
+
+  classNames: ClassNames;
+
+  template: (data: object) => HTMLOptionElement;
+
+  constructor({
+    element,
+    classNames,
+    template,
+  }: {
+    element: HTMLSelectElement;
+    classNames: ClassNames;
+    template: (data: object) => HTMLOptionElement;
+  }) {
+    super({ element, classNames });
+    this.template = template;
+  }
+
+  get placeholderOption(): HTMLOptionElement | null {
+    return (
+      this.element.querySelector('option[value=""]') ||
+      // Backward compatibility layer for the non-standard placeholder attribute supported in older versions.
+      this.element.querySelector('option[placeholder]')
+    );
+  }
+
+  get optionGroups(): Element[] {
+    return Array.from(this.element.getElementsByTagName('OPTGROUP'));
+  }
+
+  get options(): Item[] | HTMLOptionElement[] {
+    return Array.from(this.element.options);
+  }
+
+  set options(options: Item[] | HTMLOptionElement[]) {
+    const fragment = document.createDocumentFragment();
+    const addOptionToFragment = (data): void => {
+      // Create a standard select option
+      const option = this.template(data);
+      // Append it to fragment
+      fragment.appendChild(option);
+    };
+
+    // Add each list item to list
+    options.forEach((optionData) => addOptionToFragment(optionData));
+
+    this.appendDocFragment(fragment);
+  }
+
+  appendDocFragment(fragment: DocumentFragment): void {
+    this.element.innerHTML = '';
+    this.element.appendChild(fragment);
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/constants.test.ts b/my_flask_app/static/choices.js/src/scripts/constants.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..807016dfe50c30a40c35166fc8cf466f1a360ec8
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/constants.test.ts
@@ -0,0 +1,155 @@
+import { expect } from 'chai';
+import { EVENTS, ACTION_TYPES, KEY_CODES, SCROLLING_SPEED } from './constants';
+import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from './defaults';
+
+describe('constants', () => {
+  describe('type checks', () => {
+    describe('DEFAULT_CLASSNAMES', () => {
+      it('exports as an object with expected keys', () => {
+        expect(DEFAULT_CLASSNAMES).to.be.an('object');
+        expect(Object.keys(DEFAULT_CLASSNAMES)).to.eql([
+          'containerOuter',
+          'containerInner',
+          'input',
+          'inputCloned',
+          'list',
+          'listItems',
+          'listSingle',
+          'listDropdown',
+          'item',
+          'itemSelectable',
+          'itemDisabled',
+          'itemChoice',
+          'placeholder',
+          'group',
+          'groupHeading',
+          'button',
+          'activeState',
+          'focusState',
+          'openState',
+          'disabledState',
+          'highlightedState',
+          'selectedState',
+          'flippedState',
+          'loadingState',
+          'noResults',
+          'noChoices',
+        ]);
+      });
+    });
+
+    describe('DEFAULT_CONFIG', () => {
+      it('exports as an object', () => {
+        expect(DEFAULT_CONFIG).to.be.an('object');
+      });
+
+      it('has expected config options', () => {
+        expect(DEFAULT_CONFIG.items).to.be.an('array');
+        expect(DEFAULT_CONFIG.choices).to.be.an('array');
+        expect(DEFAULT_CONFIG.silent).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.renderChoiceLimit).to.be.a('number');
+        expect(DEFAULT_CONFIG.maxItemCount).to.be.a('number');
+        expect(DEFAULT_CONFIG.addItems).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.addItemFilter).to.equal(null);
+        expect(DEFAULT_CONFIG.removeItems).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.removeItemButton).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.editItems).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.allowHTML).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.duplicateItemsAllowed).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.delimiter).to.be.a('string');
+        expect(DEFAULT_CONFIG.paste).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.searchEnabled).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.searchChoices).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.searchFloor).to.be.a('number');
+        expect(DEFAULT_CONFIG.searchResultLimit).to.be.a('number');
+        expect(DEFAULT_CONFIG.searchFields).to.be.an('array');
+        expect(DEFAULT_CONFIG.position).to.be.a('string');
+        expect(DEFAULT_CONFIG.shouldSort).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.shouldSortItems).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.placeholder).to.be.a('boolean');
+        expect(DEFAULT_CONFIG.placeholderValue).to.equal(null);
+        expect(DEFAULT_CONFIG.searchPlaceholderValue).to.equal(null);
+        expect(DEFAULT_CONFIG.prependValue).to.equal(null);
+        expect(DEFAULT_CONFIG.appendValue).to.equal(null);
+        expect(DEFAULT_CONFIG.renderSelectedChoices).to.be.a('string');
+        expect(DEFAULT_CONFIG.loadingText).to.be.a('string');
+        expect(DEFAULT_CONFIG.noResultsText).to.be.a('string');
+        expect(DEFAULT_CONFIG.noChoicesText).to.be.a('string');
+        expect(DEFAULT_CONFIG.itemSelectText).to.be.a('string');
+        expect(DEFAULT_CONFIG.uniqueItemText).to.be.a('string');
+        expect(DEFAULT_CONFIG.customAddItemText).to.be.a('string');
+        expect(DEFAULT_CONFIG.addItemText).to.be.a('function');
+        expect(DEFAULT_CONFIG.maxItemText).to.be.a('function');
+        expect(DEFAULT_CONFIG.fuseOptions).to.be.an('object');
+        expect(DEFAULT_CONFIG.callbackOnInit).to.equal(null);
+        expect(DEFAULT_CONFIG.callbackOnCreateTemplates).to.equal(null);
+      });
+    });
+
+    describe('EVENTS', () => {
+      it('exports as an object with expected keys', () => {
+        expect(EVENTS).to.be.an('object');
+        expect(Object.keys(EVENTS)).to.eql([
+          'showDropdown',
+          'hideDropdown',
+          'change',
+          'choice',
+          'search',
+          'addItem',
+          'removeItem',
+          'highlightItem',
+          'highlightChoice',
+          'unhighlightItem',
+        ]);
+      });
+    });
+
+    describe('ACTION_TYPES', () => {
+      it('exports as an object with expected keys', () => {
+        expect(ACTION_TYPES).to.be.an('object');
+        expect(Object.keys(ACTION_TYPES)).to.eql([
+          'ADD_CHOICE',
+          'FILTER_CHOICES',
+          'ACTIVATE_CHOICES',
+          'CLEAR_CHOICES',
+          'ADD_GROUP',
+          'ADD_ITEM',
+          'REMOVE_ITEM',
+          'HIGHLIGHT_ITEM',
+          'CLEAR_ALL',
+          'RESET_TO',
+          'SET_IS_LOADING',
+        ]);
+      });
+    });
+
+    describe('KEY_CODES', () => {
+      it('exports as an object with expected keys', () => {
+        expect(KEY_CODES).to.be.an('object');
+        expect(Object.keys(KEY_CODES)).to.eql([
+          'BACK_KEY',
+          'DELETE_KEY',
+          'ENTER_KEY',
+          'A_KEY',
+          'ESC_KEY',
+          'UP_KEY',
+          'DOWN_KEY',
+          'PAGE_UP_KEY',
+          'PAGE_DOWN_KEY',
+        ]);
+      });
+
+      it('exports each value as a number', () => {
+        Object.keys(KEY_CODES).forEach((key) => {
+          expect(KEY_CODES[key]).to.be.a('number');
+        });
+      });
+    });
+
+    describe('SCROLLING_SPEED', () => {
+      it('exports as an number', () => {
+        expect(SCROLLING_SPEED).to.be.a('number');
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/constants.ts b/my_flask_app/static/choices.js/src/scripts/constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0acae02517ca14228f13cf0ffa671a7efa7403d3
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/constants.ts
@@ -0,0 +1,49 @@
+import { ActionType } from './interfaces/action-type';
+import { EventType } from './interfaces/event-type';
+import { KeyCodeMap } from './interfaces/keycode-map';
+
+export const EVENTS: Record<EventType, EventType> = {
+  showDropdown: 'showDropdown',
+  hideDropdown: 'hideDropdown',
+  change: 'change',
+  choice: 'choice',
+  search: 'search',
+  addItem: 'addItem',
+  removeItem: 'removeItem',
+  highlightItem: 'highlightItem',
+  highlightChoice: 'highlightChoice',
+  unhighlightItem: 'unhighlightItem',
+};
+
+export const ACTION_TYPES: Record<ActionType, ActionType> = {
+  ADD_CHOICE: 'ADD_CHOICE',
+  FILTER_CHOICES: 'FILTER_CHOICES',
+  ACTIVATE_CHOICES: 'ACTIVATE_CHOICES',
+  CLEAR_CHOICES: 'CLEAR_CHOICES',
+  ADD_GROUP: 'ADD_GROUP',
+  ADD_ITEM: 'ADD_ITEM',
+  REMOVE_ITEM: 'REMOVE_ITEM',
+  HIGHLIGHT_ITEM: 'HIGHLIGHT_ITEM',
+  CLEAR_ALL: 'CLEAR_ALL',
+  RESET_TO: 'RESET_TO',
+  SET_IS_LOADING: 'SET_IS_LOADING',
+};
+
+export const KEY_CODES: KeyCodeMap = {
+  BACK_KEY: 46,
+  DELETE_KEY: 8,
+  ENTER_KEY: 13,
+  A_KEY: 65,
+  ESC_KEY: 27,
+  UP_KEY: 38,
+  DOWN_KEY: 40,
+  PAGE_UP_KEY: 33,
+  PAGE_DOWN_KEY: 34,
+};
+
+export const TEXT_TYPE: HTMLInputElement['type'] = 'text';
+export const SELECT_ONE_TYPE: HTMLSelectElement['type'] = 'select-one';
+export const SELECT_MULTIPLE_TYPE: HTMLSelectElement['type'] =
+  'select-multiple';
+
+export const SCROLLING_SPEED = 4;
diff --git a/my_flask_app/static/choices.js/src/scripts/defaults.ts b/my_flask_app/static/choices.js/src/scripts/defaults.ts
new file mode 100644
index 0000000000000000000000000000000000000000..488cb22329f4a489152b0de942085cad5e8a2242
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/defaults.ts
@@ -0,0 +1,81 @@
+import { ClassNames } from './interfaces/class-names';
+import { Options } from './interfaces/options';
+import { sortByAlpha, sanitise } from './lib/utils';
+
+export const DEFAULT_CLASSNAMES: ClassNames = {
+  containerOuter: 'choices',
+  containerInner: 'choices__inner',
+  input: 'choices__input',
+  inputCloned: 'choices__input--cloned',
+  list: 'choices__list',
+  listItems: 'choices__list--multiple',
+  listSingle: 'choices__list--single',
+  listDropdown: 'choices__list--dropdown',
+  item: 'choices__item',
+  itemSelectable: 'choices__item--selectable',
+  itemDisabled: 'choices__item--disabled',
+  itemChoice: 'choices__item--choice',
+  placeholder: 'choices__placeholder',
+  group: 'choices__group',
+  groupHeading: 'choices__heading',
+  button: 'choices__button',
+  activeState: 'is-active',
+  focusState: 'is-focused',
+  openState: 'is-open',
+  disabledState: 'is-disabled',
+  highlightedState: 'is-highlighted',
+  selectedState: 'is-selected',
+  flippedState: 'is-flipped',
+  loadingState: 'is-loading',
+  noResults: 'has-no-results',
+  noChoices: 'has-no-choices',
+};
+
+export const DEFAULT_CONFIG: Options = {
+  items: [],
+  choices: [],
+  silent: false,
+  renderChoiceLimit: -1,
+  maxItemCount: -1,
+  addItems: true,
+  addItemFilter: null,
+  removeItems: true,
+  removeItemButton: false,
+  editItems: false,
+  allowHTML: true,
+  duplicateItemsAllowed: true,
+  delimiter: ',',
+  paste: true,
+  searchEnabled: true,
+  searchChoices: true,
+  searchFloor: 1,
+  searchResultLimit: 4,
+  searchFields: ['label', 'value'],
+  position: 'auto',
+  resetScrollPosition: true,
+  shouldSort: true,
+  shouldSortItems: false,
+  sorter: sortByAlpha,
+  placeholder: true,
+  placeholderValue: null,
+  searchPlaceholderValue: null,
+  prependValue: null,
+  appendValue: null,
+  renderSelectedChoices: 'auto',
+  loadingText: 'Loading...',
+  noResultsText: 'No results found',
+  noChoicesText: 'No choices to choose from',
+  itemSelectText: 'Press to select',
+  uniqueItemText: 'Only unique values can be added',
+  customAddItemText: 'Only values matching specific conditions can be added',
+  addItemText: (value) => `Press Enter to add <b>"${sanitise(value)}"</b>`,
+  maxItemText: (maxItemCount) => `Only ${maxItemCount} values can be added`,
+  valueComparer: (value1, value2) => value1 === value2,
+  fuseOptions: {
+    includeScore: true,
+  },
+  labelId: '',
+  callbackOnInit: null,
+  callbackOnCreateTemplates: null,
+  classNames: DEFAULT_CLASSNAMES,
+};
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/action-type.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/action-type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dbddb196c3c6455d0a46e48eed7923a44f3ee9c5
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/action-type.ts
@@ -0,0 +1,12 @@
+export type ActionType =
+  | 'ADD_CHOICE'
+  | 'FILTER_CHOICES'
+  | 'ACTIVATE_CHOICES'
+  | 'CLEAR_CHOICES'
+  | 'ADD_GROUP'
+  | 'ADD_ITEM'
+  | 'REMOVE_ITEM'
+  | 'HIGHLIGHT_ITEM'
+  | 'CLEAR_ALL'
+  | 'RESET_TO'
+  | 'SET_IS_LOADING';
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/choice.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/choice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..71ed0099ac5a524bb4e04dddb1eeb207f38e5847
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/choice.ts
@@ -0,0 +1,17 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+export interface Choice {
+  id?: number;
+  customProperties?: Record<string, any>;
+  disabled?: boolean;
+  active?: boolean;
+  elementId?: number;
+  groupId?: number;
+  keyCode?: number;
+  label: string;
+  placeholder?: boolean;
+  selected?: boolean;
+  value: any;
+  score?: number;
+  choices?: Choice[];
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/choices.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/choices.ts
new file mode 100644
index 0000000000000000000000000000000000000000..634ff0ff5bfcb2fc634db43be341d10259edb249
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/choices.ts
@@ -0,0 +1,87 @@
+import { Options } from 'deepmerge';
+import { Store } from 'redux';
+import {
+  WrappedInput,
+  WrappedSelect,
+  Container,
+  List,
+  Input,
+  Dropdown,
+} from '../components';
+import { Choice } from './choice';
+import { Group } from './group';
+import { Item } from './item';
+import { State } from './state';
+import templates from '../templates';
+
+export interface Choices {
+  initialised: boolean;
+  config: Options;
+
+  passedElement: WrappedInput | WrappedSelect;
+
+  containerOuter: Container;
+
+  containerInner: Container;
+
+  choiceList: List;
+
+  itemList: List;
+
+  input: Input;
+
+  dropdown: Dropdown;
+
+  _isTextElement: boolean;
+
+  _isSelectOneElement: boolean;
+
+  _isSelectMultipleElement: boolean;
+
+  _isSelectElement: boolean;
+
+  _store: Store;
+
+  _templates: typeof templates;
+
+  _initialState: State;
+
+  _currentState: State;
+
+  _prevState: State;
+
+  _currentValue: string;
+
+  _canSearch: boolean;
+
+  _isScrollingOnIe: boolean;
+
+  _highlightPosition: number;
+
+  _wasTap: boolean;
+
+  _isSearching: boolean;
+
+  _placeholderValue: string | null;
+
+  _baseId: string;
+
+  _direction: HTMLElement['dir'];
+
+  _idNames: {
+    itemChoice: string;
+  };
+
+  _presetGroups: Group[] | HTMLOptGroupElement[] | Element[];
+
+  _presetOptions: Item[] | HTMLOptionElement[];
+
+  _presetChoices: Partial<Choice>[];
+
+  _presetItems: Item[] | string[];
+
+  new (
+    element: string | Element | HTMLInputElement | HTMLSelectElement,
+    userConfig: Partial<Options>,
+  );
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/class-names.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/class-names.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c1c702efd45d35ab70150c16143d86449fde99d3
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/class-names.ts
@@ -0,0 +1,55 @@
+/** Classes added to HTML generated by  By default classnames follow the BEM notation. */
+export interface ClassNames {
+  /** @default 'choices' */
+  containerOuter: string;
+  /** @default 'choices__inner' */
+  containerInner: string;
+  /** @default 'choices__input' */
+  input: string;
+  /** @default 'choices__input--cloned' */
+  inputCloned: string;
+  /** @default 'choices__list' */
+  list: string;
+  /** @default 'choices__list--multiple' */
+  listItems: string;
+  /** @default 'choices__list--single' */
+  listSingle: string;
+  /** @default 'choices__list--dropdown' */
+  listDropdown: string;
+  /** @default 'choices__item' */
+  item: string;
+  /** @default 'choices__item--selectable' */
+  itemSelectable: string;
+  /** @default 'choices__item--disabled' */
+  itemDisabled: string;
+  /** @default 'choices__item--choice' */
+  itemChoice: string;
+  /** @default 'choices__placeholder' */
+  placeholder: string;
+  /** @default 'choices__group' */
+  group: string;
+  /** @default 'choices__heading' */
+  groupHeading: string;
+  /** @default 'choices__button' */
+  button: string;
+  /** @default 'is-active' */
+  activeState: string;
+  /** @default 'is-focused' */
+  focusState: string;
+  /** @default 'is-open' */
+  openState: string;
+  /** @default 'is-disabled' */
+  disabledState: string;
+  /** @default 'is-highlighted' */
+  highlightedState: string;
+  /** @default 'is-selected' */
+  selectedState: string;
+  /** @default 'is-flipped' */
+  flippedState: string;
+  /** @default 'is-loading' */
+  loadingState: string;
+  /** @default 'has-no-results' */
+  noResults: string;
+  /** @default 'has-no-choices' */
+  noChoices: string;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/event-type.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/event-type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..be7dc656665d111ef280d5b021f25725c2c9266a
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/event-type.ts
@@ -0,0 +1,11 @@
+export type EventType =
+  | 'addItem'
+  | 'removeItem'
+  | 'highlightItem'
+  | 'unhighlightItem'
+  | 'choice'
+  | 'change'
+  | 'search'
+  | 'showDropdown'
+  | 'hideDropdown'
+  | 'highlightChoice';
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/group.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/group.ts
new file mode 100644
index 0000000000000000000000000000000000000000..62851902112dd0ed134cbb248f9f2ccaf7463107
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/group.ts
@@ -0,0 +1,8 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+export interface Group {
+  id?: number;
+  active?: boolean;
+  disabled?: boolean;
+  value: any;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/index.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ddaf6f84c1a67a8374d870a4113d2f8aed950e1
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/index.ts
@@ -0,0 +1,15 @@
+export * from './action-type';
+export * from './choice';
+export * from './choices';
+export * from './class-names';
+export * from './event-type';
+export * from './group';
+export * from './item';
+export * from './keycode-map';
+export * from './notice';
+export * from './options';
+export * from './passed-element';
+export * from './passed-element-type';
+export * from './position-options-type';
+export * from './state';
+export * from './types';
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/item.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/item.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e5dbf2706f333fc7ee2363ede2bd5b9aadd55396
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/item.ts
@@ -0,0 +1,6 @@
+import { Choice } from './choice';
+
+export interface Item extends Choice {
+  choiceId?: number;
+  highlighted?: boolean;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/keycode-map.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/keycode-map.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61bf0082549c1ec663c7b2fef9bc458d87abe106
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/keycode-map.ts
@@ -0,0 +1,11 @@
+export interface KeyCodeMap {
+  BACK_KEY: 46;
+  DELETE_KEY: 8;
+  ENTER_KEY: 13;
+  A_KEY: 65;
+  ESC_KEY: 27;
+  UP_KEY: 38;
+  DOWN_KEY: 40;
+  PAGE_UP_KEY: 33;
+  PAGE_DOWN_KEY: 34;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/notice.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/notice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..357877a54b0db3df0998fdc3e2eaf395c4d21df8
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/notice.ts
@@ -0,0 +1,5 @@
+// @todo rename
+export interface Notice {
+  response: boolean;
+  notice: string;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/options.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/options.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a4e22f56d92bbc31d37565d0019c5e2b25a6296
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/options.ts
@@ -0,0 +1,507 @@
+import Fuse from 'fuse.js';
+import { Choices } from './choices';
+import { Choice } from './choice';
+import { ClassNames } from './class-names';
+import { PositionOptionsType } from './position-options-type';
+import { Types } from './types';
+
+/**
+ * Choices options interface
+ *
+ * **Terminology**
+ *
+ * - **Choice:** A choice is a value a user can select. A choice would be equivalent to the `<option></option>` element within a select input.
+ * - **Group:** A group is a collection of choices. A group should be seen as equivalent to a `<optgroup></optgroup>` element within a select input.
+ * - **Item:** An item is an inputted value **_(text input)_** or a selected choice **_(select element)_**. In the context of a select element, an item is equivelent to a selected option element: `<option value="Hello" selected></option>` whereas in the context of a text input an item is equivelant to `<input type="text" value="Hello">`
+ */
+export interface Options {
+  /**
+   * Optionally suppress console errors and warnings.
+   *
+   * **Input types affected:** text, select-single, select-multiple
+   *
+   * @default false
+   */
+  silent: boolean;
+
+  /**
+   * Add pre-selected items (see terminology) to text input.
+   *
+   * **Input types affected:** text
+   *
+   * @example
+   * ```
+   * ['value 1', 'value 2', 'value 3']
+   * ```
+   *
+   * @example
+   * ```
+   * [{
+   *    value: 'Value 1',
+   *    label: 'Label 1',
+   *    id: 1
+   *  },
+   *  {
+   *    value: 'Value 2',
+   *    label: 'Label 2',
+   *    id: 2,
+   *    customProperties: {
+   *      random: 'I am a custom property'
+   *  }
+   * }]
+   * ```
+   *
+   * @default []
+   */
+  items: string[] | Choice[];
+
+  /**
+   * Add choices (see terminology) to select input.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @example
+   * ```
+   * [{
+   *   value: 'Option 1',
+   *   label: 'Option 1',
+   *   selected: true,
+   *   disabled: false,
+   * },
+   * {
+   *   value: 'Option 2',
+   *   label: 'Option 2',
+   *   selected: false,
+   *   disabled: true,
+   *   customProperties: {
+   *     description: 'Custom description about Option 2',
+   *     random: 'Another random custom property'
+   *   },
+   * }]
+   * ```
+   *
+   * @default []
+   */
+  choices: Choice[];
+
+  /**
+   * The amount of choices to be rendered within the dropdown list `("-1" indicates no limit)`. This is useful if you have a lot of choices where it is easier for a user to use the search area to find a choice.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default -1
+   */
+  renderChoiceLimit: number;
+
+  /**
+   * The amount of items a user can input/select `("-1" indicates no limit)`.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * @default -1
+   */
+  maxItemCount: number;
+
+  /**
+   * Whether a user can add items.
+   *
+   * **Input types affected:** text
+   *
+   * @default true
+   */
+  addItems: boolean;
+
+  /**
+   * A filter that will need to pass for a user to successfully add an item.
+   *
+   * **Input types affected:** text
+   *
+   * @default null
+   */
+  addItemFilter: string | RegExp | Types.FilterFunction | null;
+
+  /**
+   * The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string.
+   *
+   * **Input types affected:** text
+   *
+   * @default
+   * ```
+   * (value) => `Press Enter to add <b>"${value}"</b>`;
+   * ```
+   */
+  addItemText: string | Types.NoticeStringFunction;
+
+  /**
+   * Whether a user can remove items.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * @default true
+   */
+  removeItems: boolean;
+
+  /**
+   * Whether each item should have a remove button.
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * @default false
+   */
+  removeItemButton: boolean;
+
+  /**
+   * Whether a user can edit items. An item's value can be edited by pressing the backspace.
+   *
+   * **Input types affected:** text
+   *
+   * @default false
+   */
+  editItems: boolean;
+
+  /**
+   * Whether HTML should be rendered in all Choices elements.
+   * If `false`, all elements (placeholder, items, etc.) will be treated as plain text.
+   * If `true`, this can be used to perform XSS scripting attacks if you load choices from a remote source.
+   *
+   * **Deprecation Warning:** This will default to `false` in a future release.
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * @default true
+   */
+  allowHTML: boolean;
+
+  /**
+   * Whether each inputted/chosen item should be unique.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * @default true
+   */
+  duplicateItemsAllowed: boolean;
+
+  /**
+   * What divides each value. The default delimiter separates each value with a comma: `"Value 1, Value 2, Value 3"`.
+   *
+   * **Input types affected:** text
+   *
+   * @default ','
+   */
+  delimiter: string;
+
+  /**
+   * Whether a user can paste into the input.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * @default true
+   */
+  paste: boolean;
+
+  /**
+   * Whether a search area should be shown.
+   *
+   * @note Multiple select boxes will always show search areas.
+   *
+   * **Input types affected:** select-one
+   *
+   * @default true
+   */
+  searchEnabled: boolean;
+
+  /**
+   * Whether choices should be filtered by input or not. If `false`, the search event will still emit, but choices will not be filtered.
+   *
+   * **Input types affected:** select-one
+   *
+   * @default true
+   */
+  searchChoices: boolean;
+
+  /**
+   * The minimum length a search value should be before choices are searched.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default 1
+   */
+  searchFloor: number;
+
+  /**
+   * The maximum amount of search results to show.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default 4
+   */
+  searchResultLimit: number;
+
+  /**
+   * Specify which fields should be used when a user is searching. If you have added custom properties to your choices, you can add these values thus: `['label', 'value', 'customProperties.example']`.
+   *
+   * Input types affected:select-one, select-multiple
+   *
+   * @default ['label', 'value']
+   */
+  searchFields: string[];
+
+  /**
+   * Whether the dropdown should appear above `(top)` or below `(bottom)` the input. By default, if there is not enough space within the window the dropdown will appear above the input, otherwise below it.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default 'auto'
+   */
+  position: PositionOptionsType;
+
+  /**
+   * Whether the scroll position should reset after adding an item.
+   *
+   * **Input types affected:** select-multiple
+   *
+   * @default true
+   */
+  resetScrollPosition: boolean;
+
+  /**
+   * Whether choices and groups should be sorted. If false, choices/groups will appear in the order they were given.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default true
+   */
+  shouldSort: boolean;
+
+  /**
+   * Whether items should be sorted. If false, items will appear in the order they were selected.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * @default false
+   */
+  shouldSortItems: boolean;
+
+  /**
+   * The function that will sort choices and items before they are displayed (unless a user is searching). By default choices and items are sorted by alphabetical order.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @example
+   * ```
+   * // Sorting via length of label from largest to smallest
+   * const example = new Choices(element, {
+   *   sorter: function(a, b) {
+   *     return b.label.length - a.label.length;
+   *   },
+   * };
+   * ```
+   *
+   * @default sortByAlpha
+   */
+  sorter: (current: Choice, next: Choice) => number;
+
+  /**
+   * Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * @note For single select boxes, the recommended way of adding a placeholder is as follows:
+   * ```
+   * <select>
+   *   <option placeholder>This is a placeholder</option>
+   *   <option>...</option>
+   *   <option>...</option>
+   *   <option>...</option>
+   * </select>
+   * ```
+   *
+   * @default true
+   */
+  placeholder: boolean;
+
+  /**
+   * The value of the inputs placeholder.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * @default null
+   */
+  placeholderValue: string | null;
+
+  /**
+   * The value of the search inputs placeholder.
+   *
+   * **Input types affected:** select-one
+   *
+   * @default null
+   */
+  searchPlaceholderValue: string | null;
+
+  /**
+   * Prepend a value to each item added/selected.
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * @default null
+   */
+  prependValue: string | null;
+
+  /**
+   * Append a value to each item added/selected.
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * @default null
+   */
+  appendValue: string | null;
+
+  /**
+   * Whether selected choices should be removed from the list. By default choices are removed when they are selected in multiple select box. To always render choices pass `always`.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default 'auto';
+   */
+  renderSelectedChoices: 'auto' | 'always';
+
+  /**
+   * The text that is shown whilst choices are being populated via AJAX.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default 'Loading...'
+   */
+  loadingText: string;
+
+  /**
+   * The text that is shown when a user's search has returned no results. Optionally pass a function returning a string.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default 'No results found'
+   */
+  noResultsText: string | Types.StringFunction;
+
+  /**
+   * The text that is shown when a user has selected all possible choices. Optionally pass a function returning a string.
+   *
+   * **Input types affected:** select-multiple
+   *
+   * @default 'No choices to choose from'
+   */
+  noChoicesText: string | Types.StringFunction;
+
+  /**
+   * The text that is shown when a user hovers over a selectable choice.
+   *
+   * **Input types affected:** select-multiple, select-one
+   *
+   * @default 'Press to select'
+   */
+  itemSelectText: string;
+
+  /**
+   * The text that is shown when a user has focus on the input but has already reached the **max item count** [https://github.com/jshjohnson/Choices#maxitemcount]. To access the max item count, pass a function with a `maxItemCount` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string.
+   *
+   * **Input types affected:** text
+   *
+   * @default
+   * ```
+   * (maxItemCount) => `Only ${maxItemCount} values can be added.`;
+   * ```
+   */
+  maxItemText: string | Types.NoticeLimitFunction;
+
+  /**
+   * If no duplicates are allowed, and the value already exists in the array.
+   *
+   * @default 'Only unique values can be added'
+   */
+  uniqueItemText: string | Types.NoticeStringFunction;
+
+  /**
+   * The text that is shown when addItemFilter is passed and it returns false
+   *
+   * **Input types affected:** text
+   *
+   * @default 'Only values matching specific conditions can be added'
+   */
+  customAddItemText: string | Types.NoticeStringFunction;
+
+  /**
+   * Compare choice and value in appropriate way (e.g. deep equality for objects). To compare choice and value, pass a function with a `valueComparer` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example).
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * @default
+   * ```
+   * (choice, item) => choice === item;
+   * ```
+   */
+  valueComparer: Types.ValueCompareFunction;
+
+  /**
+   * Classes added to HTML generated by  By default classnames follow the BEM notation.
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   */
+  classNames: ClassNames;
+
+  /**
+   * Choices uses the great Fuse library for searching. You can find more options here: https://fusejs.io/api/options.html
+   */
+  fuseOptions: Fuse.IFuseOptions<Choices>;
+
+  /**
+   * ID of the connected label to improve a11y. If set, aria-labelledby will be added.
+   */
+  labelId: string;
+
+  /**
+   * Function to run once Choices initialises.
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * @note For each callback, this refers to the current instance of  This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`.
+   *
+   * @default null
+   */
+  callbackOnInit: ((this: Choices) => void) | null;
+
+  /**
+   * Function to run on template creation. Through this callback it is possible to provide custom templates for the various components of Choices (see terminology). For Choices to work with custom templates, it is important you maintain the various data attributes defined here [https://github.com/jshjohnson/Choices/blob/67f29c286aa21d88847adfcd6304dc7d068dc01f/assets/scripts/src/choices.js#L1993-L2067].
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * @note For each callback, this refers to the current instance of  This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`.
+   *
+   * @example
+   * ```
+   * const example = new Choices(element, {
+   *   callbackOnCreateTemplates: function (template) {
+   *     var classNames = this.config.classNames;
+   *     return {
+   *       item: (data) => {
+   *         return template(`
+   *           <div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : classNames.itemSelectable}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''}>
+   *             <span>&bigstar;</span> ${data.label}
+   *           </div>
+   *         `);
+   *       },
+   *       choice: (data) => {
+   *         return template(`
+   *           <div class="${classNames.item} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId > 0 ? 'role="treeitem"' : 'role="option"'}>
+   *             <span>&bigstar;</span> ${data.label}
+   *           </div>
+   *         `);
+   *       },
+   *     };
+   *   }
+   * });
+   * ```
+   *
+   * @default null
+   */
+  callbackOnCreateTemplates: ((template: Types.StrToEl) => void) | null;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/passed-element-type.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/passed-element-type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..492258227ba5f062ffae21047969c28cc06106c3
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/passed-element-type.ts
@@ -0,0 +1 @@
+export type PassedElementType = 'text' | 'select-one' | 'select-multiple';
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/passed-element.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/passed-element.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0aa73b0fe825317460699ceb190f053768476af7
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/passed-element.ts
@@ -0,0 +1,138 @@
+import { Choices } from './choices';
+import { Choice } from './choice';
+import { ClassNames } from './class-names';
+import { EventType } from './event-type';
+import { PassedElementType } from './passed-element-type';
+
+export interface PassedElement extends HTMLElement {
+  classNames: ClassNames;
+  element: (HTMLInputElement | HTMLSelectElement) & {
+    // Extends HTMLElement addEventListener with Choices events
+    addEventListener<K extends EventType>(
+      type: K,
+      listener: (
+        this: HTMLInputElement | HTMLSelectElement,
+        ev: EventMap[K],
+      ) => void,
+      options?: boolean | AddEventListenerOptions,
+    ): void;
+  };
+  type: PassedElementType;
+  isDisabled: boolean;
+  parentInstance: Choices;
+}
+
+/**
+ * Events fired by Choices behave the same as standard events. Each event is triggered on the element passed to Choices (accessible via `this.passedElement`. Arguments are accessible within the `event.detail` object.
+ */
+export interface EventMap {
+  /**
+   * Triggered each time an item is added (programmatically or by the user).
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * Arguments: id, value, label, groupValue, keyCode
+   */
+  addItem: CustomEvent<{
+    id: number;
+    value: string;
+    label: string;
+    groupValue: string;
+    keyCode: number;
+  }>;
+
+  /**
+   * Triggered each time an item is removed (programmatically or by the user).
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * Arguments: id, value, label, groupValue
+   */
+  removeItem: CustomEvent<{
+    id: number;
+    value: string;
+    label: string;
+    groupValue: string;
+  }>;
+
+  /**
+   * Triggered each time an item is highlighted.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * Arguments: id, value, label, groupValue
+   */
+  highlightItem: CustomEvent<{
+    id: number;
+    value: string;
+    label: string;
+    groupValue: string;
+  }>;
+
+  /**
+   * Triggered each time an item is unhighlighted.
+   *
+   * **Input types affected:** text, select-multiple
+   *
+   * Arguments: id, value, label, groupValue
+   */
+  unhighlightItem: CustomEvent<{
+    id: number;
+    value: string;
+    label: string;
+    groupValue: string;
+  }>;
+
+  /**
+   * Triggered each time a choice is selected **by a user**, regardless if it changes the value of the input.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * Arguments: choice: Choice
+   */
+  choice: CustomEvent<{ choice: Choice }>;
+
+  /**
+   * Triggered each time an item is added/removed **by a user**.
+   *
+   * **Input types affected:** text, select-one, select-multiple
+   *
+   * Arguments: value
+   */
+  change: CustomEvent<{ value: string }>;
+
+  /**
+   * Triggered when a user types into an input to search choices.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * Arguments: value, resultCount
+   */
+  search: CustomEvent<{ value: string; resultCount: number }>;
+
+  /**
+   * Triggered when the dropdown is shown.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * Arguments: -
+   */
+  showDropdown: CustomEvent<undefined>;
+
+  /**
+   * Triggered when the dropdown is hidden.
+   *
+   * **Input types affected:** select-one, select-multiple
+   *
+   * Arguments: -
+   */
+  hideDropdown: CustomEvent<undefined>;
+
+  /**
+   * Triggered when a choice from the dropdown is highlighted.
+   *
+   * Input types affected: select-one, select-multiple
+   * Arguments: el is the choice.passedElement that was affected.
+   */
+  highlightChoice: CustomEvent<{ el: PassedElement }>;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/position-options-type.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/position-options-type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d23b4e6e599fa001f07437f63f6ae1fc4e82f0df
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/position-options-type.ts
@@ -0,0 +1 @@
+export type PositionOptionsType = 'auto' | 'top' | 'bottom';
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/state.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/state.ts
new file mode 100644
index 0000000000000000000000000000000000000000..19f49a17768c7ff6f39b35fa177f02883f373b7b
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/state.ts
@@ -0,0 +1,10 @@
+import { Choice } from './choice';
+import { Group } from './group';
+import { Item } from './item';
+
+export interface State {
+  choices: Choice[];
+  groups: Group[];
+  items: Item[];
+  loading: boolean;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/interfaces/types.ts b/my_flask_app/static/choices.js/src/scripts/interfaces/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe0f46edbc72be44f8afd378f54c4551da657d4f
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/interfaces/types.ts
@@ -0,0 +1,13 @@
+export namespace Types {
+  export type StrToEl = (
+    str: string,
+  ) => HTMLElement | HTMLInputElement | HTMLOptionElement;
+  export type StringFunction = () => string;
+  export type NoticeStringFunction = (value: string) => string;
+  export type NoticeLimitFunction = (maxItemCount: number) => string;
+  export type FilterFunction = (value: string) => boolean;
+  export type ValueCompareFunction = (
+    value1: string,
+    value2: string,
+  ) => boolean;
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/lib/utils.test.ts b/my_flask_app/static/choices.js/src/scripts/lib/utils.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..022a2cf4fca67b88c82bc783b3af067df2e4ccd1
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/lib/utils.test.ts
@@ -0,0 +1,281 @@
+/* eslint-disable no-new-wrappers */
+import { expect } from 'chai';
+import { stub } from 'sinon';
+
+import {
+  cloneObject,
+  diff,
+  dispatchEvent,
+  existsInArray,
+  generateChars,
+  generateId,
+  getRandomNumber,
+  getType,
+  isType,
+  sanitise,
+  sortByAlpha,
+  sortByScore,
+  parseCustomProperties,
+} from './utils';
+
+describe('utils', () => {
+  describe('getRandomNumber', () => {
+    it('returns random number between range', () => {
+      for (let index = 0; index < 10; index++) {
+        const output = getRandomNumber(1, 10);
+        expect(output).to.be.a('number');
+        expect(output).to.be.within(1, 10);
+      }
+    });
+  });
+
+  describe('generateChars', () => {
+    it('generates a string of random chars with given length', () => {
+      const output = generateChars(10);
+      expect(output).to.be.a('string');
+      expect(output).to.have.length(10);
+    });
+  });
+
+  describe('generateId', () => {
+    describe('when given element has id value', () => {
+      it('generates a unique prefixed id based on given elements id', () => {
+        const element = document.createElement('select');
+        element.id = 'test-id';
+        const prefix = 'test-prefix';
+
+        const output = generateId(element, prefix);
+
+        expect(output).to.equal(`${prefix}-${element.id}`);
+      });
+    });
+
+    describe('when given element has no id value but name value', () => {
+      it('generates a unique prefixed id based on given elements name plus 2 random characters', () => {
+        const element = document.createElement('select');
+        element.name = 'test-name';
+        const prefix = 'test-prefix';
+
+        const output = generateId(element, prefix);
+        const expectedOutput = `${prefix}-${element.name}-`;
+
+        expect(output).to.contain(expectedOutput);
+        expect(output).to.have.length(expectedOutput.length + 2);
+      });
+    });
+
+    describe('when given element has no id value and no name value', () => {
+      it('generates a unique prefixed id based on 4 random characters', () => {
+        const element = document.createElement('select');
+        const prefix = 'test-prefix';
+
+        const output = generateId(element, prefix);
+        const expectedOutput = `${prefix}-`;
+
+        expect(output).to.contain(expectedOutput);
+        expect(output).to.have.length(expectedOutput.length + 4);
+      });
+    });
+  });
+
+  describe('getType', () => {
+    it('returns type of given object', () => {
+      expect(getType({})).to.equal('Object');
+      expect(getType(1)).to.equal('Number');
+      expect(getType(true)).to.equal('Boolean');
+      expect(getType([])).to.equal('Array');
+      expect(getType(() => {})).to.equal('Function');
+      expect(getType(new Error())).to.equal('Error');
+      expect(getType(/''/g)).to.equal('RegExp');
+      expect(getType(new String())).to.equal('String'); // eslint-disable-line
+      expect(getType('')).to.equal('String');
+    });
+  });
+
+  describe('isType', () => {
+    it('checks with given object type equals given type', () => {
+      expect(isType('Object', {})).to.equal(true);
+      expect(isType('String', {})).to.equal(false);
+    });
+  });
+
+  describe('sanitise', () => {
+    describe('when passing a parameter that is not a string', () => {
+      it('returns the passed argument', () => {
+        const value = {
+          test: true,
+        };
+        const output = sanitise(value);
+        expect(output).to.equal(value);
+      });
+    });
+
+    describe('when passing a string', () => {
+      it('strips HTML from value', () => {
+        const value = '<script>somethingMalicious();</script>';
+        const output = sanitise(value);
+        expect(output).to.equal(
+          '&lt;script&gt;somethingMalicious();&lt;/script&gt;',
+        );
+      });
+    });
+  });
+
+  describe('sortByAlpha', () => {
+    describe('sorting an array', () => {
+      it('sorts by value alphabetically', () => {
+        const values = [
+          { value: 'The Strokes' },
+          { value: 'Arctic Monkeys' },
+          { value: 'Oasis' },
+          { value: 'Tame Impala' },
+        ];
+
+        const output = values.sort(sortByAlpha);
+
+        expect(output).to.eql([
+          { value: 'Arctic Monkeys' },
+          { value: 'Oasis' },
+          { value: 'Tame Impala' },
+          { value: 'The Strokes' },
+        ]);
+      });
+
+      it('sorts by label alphabetically', () => {
+        const values = [
+          { value: '0', label: 'The Strokes' },
+          { value: '0', label: 'Arctic Monkeys' },
+          { value: '0', label: 'Oasis' },
+          { value: '0', label: 'Tame Impala' },
+        ];
+
+        const output = values.sort(sortByAlpha);
+
+        expect(output).to.eql([
+          { value: '0', label: 'Arctic Monkeys' },
+          { value: '0', label: 'Oasis' },
+          { value: '0', label: 'Tame Impala' },
+          { value: '0', label: 'The Strokes' },
+        ]);
+      });
+    });
+  });
+
+  describe('sortByScore', () => {
+    describe('sorting an array', () => {
+      it('sorts by score ascending', () => {
+        const values = [
+          { score: 10 },
+          { score: 3001 },
+          { score: 124 },
+          { score: 400 },
+        ];
+
+        const output = values.sort(sortByScore);
+
+        expect(output).to.eql([
+          { score: 10 },
+          { score: 124 },
+          { score: 400 },
+          { score: 3001 },
+        ]);
+      });
+    });
+  });
+
+  describe('dispatchEvent', () => {
+    it('dispatches custom event of given type on given element', () => {
+      const fakeElement = {
+        dispatchEvent: stub(),
+      };
+      const eventType = 'addItem';
+      const customArgs = {
+        testing: true,
+      };
+
+      dispatchEvent(fakeElement as any, eventType, customArgs);
+
+      expect(fakeElement.dispatchEvent.called).to.equal(true);
+      const event = fakeElement.dispatchEvent.lastCall.args[0];
+      expect(event).to.be.instanceof(CustomEvent);
+      expect(event.bubbles).to.equal(true);
+      expect(event.cancelable).to.equal(true);
+      expect(event.detail).to.equal(customArgs);
+    });
+  });
+
+  describe('existsInArray', () => {
+    it('determines whether a value exists within given array', () => {
+      const values = [
+        { value: 'The Strokes' },
+        { value: 'Arctic Monkeys' },
+        { value: 'Oasis' },
+        { value: 'Tame Impala' },
+      ];
+
+      expect(existsInArray(values, 'Oasis', 'value')).to.equal(true);
+      expect(existsInArray(values, 'The Beatles', 'value')).to.equal(false);
+    });
+  });
+
+  describe('cloneObject', () => {
+    it('deeply clones a given object', () => {
+      const object = {
+        levelOne: {
+          id: 1,
+          levelTwo: {
+            id: 2,
+            levelThree: {
+              id: 3,
+              levelFour: {
+                id: 4,
+              },
+            },
+          },
+        },
+      };
+
+      const output = cloneObject(object);
+
+      expect(output).to.not.equal(object);
+      expect(output).to.eql(object);
+    });
+  });
+
+  describe('diff', () => {
+    it('returns an array of keys present on the first but missing on the second object', () => {
+      const obj1 = {
+        foo: 'bar',
+        baz: 'foo',
+      };
+      const obj2 = {
+        foo: 'bar',
+      };
+
+      const output = diff(obj1, obj2);
+
+      expect(output).to.deep.equal(['baz']);
+    });
+  });
+
+  describe('_parseCustomProperties', () => {
+    describe('when custom properties are valid json', () => {
+      it('returns the properties as object', () => {
+        const customProperties = '{"description": "foo", "bar": "foo"}';
+        const result = { description: 'foo', bar: 'foo' };
+
+        const value = parseCustomProperties(customProperties);
+        expect(value).to.eql(result);
+      });
+    });
+    describe('when custom properties are undefined', () => {
+      it('returns an empty object', () => {
+        const customProperties = undefined;
+        const result = {};
+
+        const value = parseCustomProperties(customProperties);
+        expect(value).to.eql(result);
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/lib/utils.ts b/my_flask_app/static/choices.js/src/scripts/lib/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba587bcb5a8bde359af104b301988870bf4dbed0
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/lib/utils.ts
@@ -0,0 +1,194 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import { Choice } from '../interfaces/choice';
+import { EventType } from '../interfaces/event-type';
+
+export const getRandomNumber = (min: number, max: number): number =>
+  Math.floor(Math.random() * (max - min) + min);
+
+export const generateChars = (length: number): string =>
+  Array.from({ length }, () => getRandomNumber(0, 36).toString(36)).join('');
+
+export const generateId = (
+  element: HTMLInputElement | HTMLSelectElement,
+  prefix: string,
+): string => {
+  let id =
+    element.id ||
+    (element.name && `${element.name}-${generateChars(2)}`) ||
+    generateChars(4);
+  id = id.replace(/(:|\.|\[|\]|,)/g, '');
+  id = `${prefix}-${id}`;
+
+  return id;
+};
+
+export const getType = (obj: any): string =>
+  Object.prototype.toString.call(obj).slice(8, -1);
+
+export const isType = (type: string, obj: any): boolean =>
+  obj !== undefined && obj !== null && getType(obj) === type;
+
+export const wrap = (
+  element: HTMLElement,
+  wrapper: HTMLElement = document.createElement('div'),
+): HTMLElement => {
+  if (element.parentNode) {
+    if (element.nextSibling) {
+      element.parentNode.insertBefore(wrapper, element.nextSibling);
+    } else {
+      element.parentNode.appendChild(wrapper);
+    }
+  }
+
+  return wrapper.appendChild(element);
+};
+
+export const getAdjacentEl = (
+  startEl: Element,
+  selector: string,
+  direction = 1,
+): Element => {
+  const prop = `${direction > 0 ? 'next' : 'previous'}ElementSibling`;
+
+  let sibling = startEl[prop];
+  while (sibling) {
+    if (sibling.matches(selector)) {
+      return sibling;
+    }
+    sibling = sibling[prop];
+  }
+
+  return sibling;
+};
+
+export const isScrolledIntoView = (
+  element: HTMLElement,
+  parent: HTMLElement,
+  direction = 1,
+): boolean => {
+  if (!element) {
+    return false;
+  }
+
+  let isVisible;
+
+  if (direction > 0) {
+    // In view from bottom
+    isVisible =
+      parent.scrollTop + parent.offsetHeight >=
+      element.offsetTop + element.offsetHeight;
+  } else {
+    // In view from top
+    isVisible = element.offsetTop >= parent.scrollTop;
+  }
+
+  return isVisible;
+};
+
+export const sanitise = <T>(value: T | string): T | string => {
+  if (typeof value !== 'string') {
+    return value;
+  }
+
+  return value
+    .replace(/&/g, '&amp;')
+    .replace(/>/g, '&gt;')
+    .replace(/</g, '&lt;')
+    .replace(/"/g, '&quot;');
+};
+
+export const strToEl = ((): ((str: string) => Element) => {
+  const tmpEl = document.createElement('div');
+
+  return (str): Element => {
+    const cleanedInput = str.trim();
+    tmpEl.innerHTML = cleanedInput;
+    const firldChild = tmpEl.children[0];
+
+    while (tmpEl.firstChild) {
+      tmpEl.removeChild(tmpEl.firstChild);
+    }
+
+    return firldChild;
+  };
+})();
+
+interface RecordToCompare {
+  value: string;
+  label?: string;
+}
+export const sortByAlpha = (
+  { value, label = value }: RecordToCompare,
+  { value: value2, label: label2 = value2 }: RecordToCompare,
+): number =>
+  label.localeCompare(label2, [], {
+    sensitivity: 'base',
+    ignorePunctuation: true,
+    numeric: true,
+  });
+
+export const sortByScore = (
+  a: Pick<Choice, 'score'>,
+  b: Pick<Choice, 'score'>,
+): number => {
+  const { score: scoreA = 0 } = a;
+  const { score: scoreB = 0 } = b;
+
+  return scoreA - scoreB;
+};
+
+export const dispatchEvent = (
+  element: HTMLElement,
+  type: EventType,
+  customArgs: object | null = null,
+): boolean => {
+  const event = new CustomEvent(type, {
+    detail: customArgs,
+    bubbles: true,
+    cancelable: true,
+  });
+
+  return element.dispatchEvent(event);
+};
+
+export const existsInArray = (
+  array: any[],
+  value: string,
+  key = 'value',
+): boolean =>
+  array.some((item) => {
+    if (typeof value === 'string') {
+      return item[key] === value.trim();
+    }
+
+    return item[key] === value;
+  });
+
+export const cloneObject = (obj: object): object =>
+  JSON.parse(JSON.stringify(obj));
+
+/**
+ * Returns an array of keys present on the first but missing on the second object
+ */
+export const diff = (
+  a: Record<string, any>,
+  b: Record<string, any>,
+): string[] => {
+  const aKeys = Object.keys(a).sort();
+  const bKeys = Object.keys(b).sort();
+
+  return aKeys.filter((i) => bKeys.indexOf(i) < 0);
+};
+
+export const parseCustomProperties = (customProperties): any => {
+  if (typeof customProperties !== 'undefined') {
+    try {
+      return JSON.parse(customProperties);
+    } catch (e) {
+      return customProperties;
+    }
+  }
+
+  return {};
+};
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/choices.test.ts b/my_flask_app/static/choices.js/src/scripts/reducers/choices.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..247525bd5a49b3e524cfbae44842c534db7ab87a
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/choices.test.ts
@@ -0,0 +1,295 @@
+import { expect } from 'chai';
+import { Choice } from '../interfaces/choice';
+import choices, { defaultState } from './choices';
+
+describe('reducers/choices', () => {
+  it('should return same state when no action matches', () => {
+    expect(choices(defaultState, {} as any)).to.equal(defaultState);
+  });
+
+  describe('when choices do not exist', () => {
+    describe('ADD_CHOICE', () => {
+      const value = 'test';
+      const label = 'test';
+      const id = 1;
+      const groupId = 1;
+      const disabled = false;
+      const elementId = 1;
+      const customProperties = { test: true };
+      const placeholder = true;
+
+      describe('passing expected values', () => {
+        it('adds choice', () => {
+          const expectedResponse = [
+            {
+              value,
+              label,
+              id,
+              groupId,
+              disabled,
+              elementId,
+              customProperties,
+              placeholder,
+              selected: false,
+              active: true,
+              score: 9999,
+            },
+          ];
+
+          const actualResponse = choices(undefined, {
+            type: 'ADD_CHOICE',
+            value,
+            label,
+            id,
+            groupId,
+            disabled,
+            elementId,
+            customProperties,
+            placeholder,
+          });
+
+          expect(actualResponse).to.eql(expectedResponse);
+        });
+      });
+
+      describe('fallback values', () => {
+        describe('passing no label', () => {
+          it('adds choice using value as label', () => {
+            const expectedResponse = [
+              {
+                value,
+                label: value,
+                id,
+                groupId,
+                disabled,
+                elementId,
+                customProperties,
+                placeholder,
+                selected: false,
+                active: true,
+                score: 9999,
+              },
+            ];
+
+            const actualResponse = choices(undefined, {
+              type: 'ADD_CHOICE',
+              value,
+              label: undefined,
+              id,
+              groupId,
+              disabled,
+              elementId,
+              customProperties,
+              placeholder,
+            });
+
+            expect(actualResponse).to.eql(expectedResponse);
+          });
+        });
+
+        describe('passing no placeholder value', () => {
+          it('adds choice with placeholder set to false', () => {
+            const expectedResponse = [
+              {
+                value,
+                label: value,
+                id,
+                groupId,
+                disabled,
+                elementId,
+                customProperties,
+                placeholder: false,
+                selected: false,
+                active: true,
+                score: 9999,
+              },
+            ];
+
+            const actualResponse = choices(undefined, {
+              type: 'ADD_CHOICE',
+              value,
+              label: undefined,
+              id,
+              groupId,
+              disabled,
+              elementId,
+              customProperties,
+              placeholder: undefined,
+            });
+
+            expect(actualResponse).to.eql(expectedResponse);
+          });
+        });
+      });
+    });
+  });
+
+  describe('when choices exist', () => {
+    let state;
+
+    beforeEach(() => {
+      state = [
+        {
+          id: 1,
+          elementId: 'choices-test-1',
+          groupId: -1,
+          value: 'Choice 1',
+          label: 'Choice 1',
+          disabled: false,
+          selected: false,
+          active: false,
+          score: 9999,
+          customProperties: null,
+          placeholder: false,
+        },
+        {
+          id: 2,
+          elementId: 'choices-test-2',
+          groupId: -1,
+          value: 'Choice 2',
+          label: 'Choice 2',
+          disabled: false,
+          selected: true,
+          active: false,
+          score: 9999,
+          customProperties: null,
+          placeholder: false,
+        },
+      ];
+    });
+
+    describe('FILTER_CHOICES', () => {
+      it('sets active flag based on whether choice is in passed results', () => {
+        const id = 1;
+        const score = 10;
+        const active = true;
+
+        const expectedResponse = {
+          ...state[0],
+          active,
+          score,
+        };
+
+        const actualResponse = choices(state, {
+          type: 'FILTER_CHOICES',
+          results: [
+            {
+              item: { id } as Choice,
+              score,
+            },
+          ],
+        }).find((choice) => choice.id === id);
+
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+    });
+
+    describe('ACTIVATE_CHOICES', () => {
+      it('sets active flag to passed value', () => {
+        const clonedState = state.slice(0);
+
+        const expectedResponse = [
+          {
+            ...state[0],
+            active: true,
+          },
+          {
+            ...state[1],
+            active: true,
+          },
+        ];
+
+        const actualResponse = choices(clonedState, {
+          type: 'ACTIVATE_CHOICES',
+          active: true,
+        });
+
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+    });
+
+    describe('CLEAR_CHOICES', () => {
+      it('restores to defaultState', () => {
+        const clonedState = state.slice(0);
+        const expectedResponse = defaultState;
+        const actualResponse = choices(clonedState, {
+          type: 'CLEAR_CHOICES',
+        });
+
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+    });
+
+    describe('ADD_ITEM', () => {
+      describe('when action has a choice id', () => {
+        it('disables choice corresponding with id', () => {
+          const id = 2;
+          const clonedState = state.slice(0);
+          const expectedResponse = [
+            {
+              ...state[0],
+            },
+            {
+              ...state[1],
+              selected: true,
+            },
+          ];
+
+          const actualResponse = choices(clonedState, {
+            type: 'ADD_ITEM',
+            choiceId: id,
+          });
+
+          expect(actualResponse).to.eql(expectedResponse);
+        });
+      });
+
+      describe('when action has no choice id', () => {
+        it('returns state', () => {
+          const clonedState = state.slice(0);
+          const actualResponse = choices(clonedState, {
+            type: 'ADD_ITEM',
+            choiceId: undefined,
+          });
+
+          expect(actualResponse).to.equal(clonedState);
+        });
+      });
+    });
+
+    describe('REMOVE_ITEM', () => {
+      it('selects choice by passed id', () => {
+        const id = 2;
+        const clonedState = state.slice(0);
+        const expectedResponse = [
+          {
+            ...state[0],
+          },
+          {
+            ...state[1],
+            selected: false,
+          },
+        ];
+
+        const actualResponse = choices(clonedState, {
+          type: 'REMOVE_ITEM',
+          choiceId: id,
+        });
+
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+
+      describe('passing no id', () => {
+        it('returns state', () => {
+          const clonedState = state.slice(0);
+          const actualResponse = choices(clonedState, {
+            type: 'REMOVE_ITEM',
+            choiceId: undefined,
+          });
+
+          expect(actualResponse).to.equal(clonedState);
+        });
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/choices.ts b/my_flask_app/static/choices.js/src/scripts/reducers/choices.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7cd1c6251dcfb76200067ee37e2dd9fa6d6ffe78
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/choices.ts
@@ -0,0 +1,128 @@
+import {
+  AddChoiceAction,
+  FilterChoicesAction,
+  ActivateChoicesAction,
+  ClearChoicesAction,
+} from '../actions/choices';
+import { AddItemAction, RemoveItemAction } from '../actions/items';
+import { Choice } from '../interfaces/choice';
+
+export const defaultState = [];
+
+type ActionTypes =
+  | AddChoiceAction
+  | FilterChoicesAction
+  | ActivateChoicesAction
+  | ClearChoicesAction
+  | AddItemAction
+  | RemoveItemAction
+  | Record<string, never>;
+
+export default function choices(
+  state: Choice[] = defaultState,
+  action: ActionTypes = {},
+): Choice[] {
+  switch (action.type) {
+    case 'ADD_CHOICE': {
+      const addChoiceAction = action as AddChoiceAction;
+      const choice = {
+        id: addChoiceAction.id,
+        elementId: addChoiceAction.elementId,
+        groupId: addChoiceAction.groupId,
+        value: addChoiceAction.value,
+        label: addChoiceAction.label || addChoiceAction.value,
+        disabled: addChoiceAction.disabled || false,
+        selected: false,
+        active: true,
+        score: 9999,
+        customProperties: addChoiceAction.customProperties,
+        placeholder: addChoiceAction.placeholder || false,
+      };
+
+      /*
+        A disabled choice appears in the choice dropdown but cannot be selected
+        A selected choice has been added to the passed input's value (added as an item)
+        An active choice appears within the choice dropdown
+      */
+      return [...state, choice as Choice];
+    }
+
+    case 'ADD_ITEM': {
+      const addItemAction = action as AddItemAction;
+
+      // When an item is added and it has an associated choice,
+      // we want to disable it so it can't be chosen again
+      if (addItemAction.choiceId > -1) {
+        return state.map((obj) => {
+          const choice = obj;
+          if (choice.id === parseInt(`${addItemAction.choiceId}`, 10)) {
+            choice.selected = true;
+          }
+
+          return choice;
+        });
+      }
+
+      return state;
+    }
+
+    case 'REMOVE_ITEM': {
+      const removeItemAction = action as RemoveItemAction;
+
+      // When an item is removed and it has an associated choice,
+      // we want to re-enable it so it can be chosen again
+      if (removeItemAction.choiceId && removeItemAction.choiceId > -1) {
+        return state.map((obj) => {
+          const choice = obj;
+          if (choice.id === parseInt(`${removeItemAction.choiceId}`, 10)) {
+            choice.selected = false;
+          }
+
+          return choice;
+        });
+      }
+
+      return state;
+    }
+
+    case 'FILTER_CHOICES': {
+      const filterChoicesAction = action as FilterChoicesAction;
+
+      return state.map((obj) => {
+        const choice = obj;
+        // Set active state based on whether choice is
+        // within filtered results
+        choice.active = filterChoicesAction.results.some(({ item, score }) => {
+          if (item.id === choice.id) {
+            choice.score = score;
+
+            return true;
+          }
+
+          return false;
+        });
+
+        return choice;
+      });
+    }
+
+    case 'ACTIVATE_CHOICES': {
+      const activateChoicesAction = action as ActivateChoicesAction;
+
+      return state.map((obj) => {
+        const choice = obj;
+        choice.active = activateChoicesAction.active;
+
+        return choice;
+      });
+    }
+
+    case 'CLEAR_CHOICES': {
+      return defaultState;
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/groups.test.ts b/my_flask_app/static/choices.js/src/scripts/reducers/groups.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..95008efb6ad3d801091a785d22d5875af20d221e
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/groups.test.ts
@@ -0,0 +1,71 @@
+import { expect } from 'chai';
+import groups, { defaultState } from './groups';
+
+describe('reducers/groups', () => {
+  it('should return same state when no action matches', () => {
+    expect(groups(defaultState, {} as any)).to.equal(defaultState);
+  });
+
+  describe('when groups do not exist', () => {
+    describe('ADD_GROUP', () => {
+      it('adds group', () => {
+        const id = 1;
+        const value = 'Group one';
+        const active = true;
+        const disabled = false;
+
+        const expectedResponse = [
+          {
+            id,
+            value,
+            active,
+            disabled,
+          },
+        ];
+
+        const actualResponse = groups(undefined, {
+          type: 'ADD_GROUP',
+          id,
+          value,
+          active,
+          disabled,
+        });
+
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+    });
+  });
+
+  describe('when groups exist', () => {
+    let state;
+
+    beforeEach(() => {
+      state = [
+        {
+          id: 1,
+          value: 'Group one',
+          active: true,
+          disabled: false,
+        },
+        {
+          id: 2,
+          value: 'Group two',
+          active: true,
+          disabled: false,
+        },
+      ];
+    });
+
+    describe('CLEAR_CHOICES', () => {
+      it('restores to defaultState', () => {
+        const clonedState = state.slice(0);
+        const expectedResponse = defaultState;
+        const actualResponse = groups(clonedState, {
+          type: 'CLEAR_CHOICES',
+        });
+
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/groups.ts b/my_flask_app/static/choices.js/src/scripts/reducers/groups.ts
new file mode 100644
index 0000000000000000000000000000000000000000..171404c1037d691f6a6312ec0f3c8f10990ee7e3
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/groups.ts
@@ -0,0 +1,37 @@
+import { AddGroupAction } from '../actions/groups';
+import { ClearChoicesAction } from '../actions/choices';
+import { Group } from '../interfaces/group';
+import { State } from '../interfaces/state';
+
+export const defaultState = [];
+
+type ActionTypes = AddGroupAction | ClearChoicesAction | Record<string, never>;
+
+export default function groups(
+  state: Group[] = defaultState,
+  action: ActionTypes = {},
+): State['groups'] {
+  switch (action.type) {
+    case 'ADD_GROUP': {
+      const addGroupAction = action as AddGroupAction;
+
+      return [
+        ...state,
+        {
+          id: addGroupAction.id,
+          value: addGroupAction.value,
+          active: addGroupAction.active,
+          disabled: addGroupAction.disabled,
+        },
+      ];
+    }
+
+    case 'CLEAR_CHOICES': {
+      return [];
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/index.test.ts b/my_flask_app/static/choices.js/src/scripts/reducers/index.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..50e252cf31fd643be7c8aaedf714f4b1f4fcbc3c
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/index.test.ts
@@ -0,0 +1,60 @@
+import { createStore } from 'redux';
+import { expect } from 'chai';
+import rootReducer from '.';
+import groups from './groups';
+import choices from './choices';
+import items from './items';
+import loading from './loading';
+
+describe('reducers/rootReducer', () => {
+  const store = createStore(rootReducer);
+
+  it('returns expected reducers', () => {
+    const state = store.getState();
+
+    expect(state.groups).to.equal(groups(undefined, {} as any));
+    expect(state.choices).to.equal(choices(undefined, {} as any));
+    expect(state.items).to.equal(items(undefined, {} as any));
+    expect(state.loading).to.equal(loading(undefined, {} as any));
+  });
+
+  describe('CLEAR_ALL', () => {
+    it('resets state', () => {
+      const output = rootReducer(
+        {
+          items: [1, 2, 3],
+          groups: [1, 2, 3],
+          choices: [1, 2, 3],
+        },
+        {
+          type: 'CLEAR_ALL',
+        },
+      );
+
+      expect(output).to.eql({
+        items: [],
+        groups: [],
+        choices: [],
+        loading: false,
+      });
+    });
+  });
+
+  describe('RESET_TO', () => {
+    it('replaces state with given state', () => {
+      const output = rootReducer(
+        {
+          items: [1, 2, 3],
+          groups: [1, 2, 3],
+          choices: [1, 2, 3],
+        },
+        {
+          type: 'RESET_TO',
+          state: {},
+        },
+      );
+
+      expect(output).to.eql({});
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/index.ts b/my_flask_app/static/choices.js/src/scripts/reducers/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92b10e4427eeebac8b01ceb2d3d6a724a7c87cdf
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/index.ts
@@ -0,0 +1,37 @@
+import { combineReducers } from 'redux';
+import items from './items';
+import groups from './groups';
+import choices from './choices';
+import loading from './loading';
+import { cloneObject } from '../lib/utils';
+
+export const defaultState = {
+  groups: [],
+  items: [],
+  choices: [],
+  loading: false,
+};
+
+const appReducer = combineReducers({
+  items,
+  groups,
+  choices,
+  loading,
+});
+
+const rootReducer = (passedState, action): object => {
+  let state = passedState;
+  // If we are clearing all items, groups and options we reassign
+  // state and then pass that state to our proper reducer. This isn't
+  // mutating our actual state
+  // See: http://stackoverflow.com/a/35641992
+  if (action.type === 'CLEAR_ALL') {
+    state = defaultState;
+  } else if (action.type === 'RESET_TO') {
+    return cloneObject(action.state);
+  }
+
+  return appReducer(state, action);
+};
+
+export default rootReducer;
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/items.test.ts b/my_flask_app/static/choices.js/src/scripts/reducers/items.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d8793ece7151c7ae67bbe9fe84fde7d7141067c7
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/items.test.ts
@@ -0,0 +1,182 @@
+import { expect } from 'chai';
+import items, { defaultState } from './items';
+import { RemoveItemAction } from '../actions/items';
+
+describe('reducers/items', () => {
+  it('should return same state when no action matches', () => {
+    expect(items(defaultState, {} as any)).to.equal(defaultState);
+  });
+
+  describe('when items do not exist', () => {
+    describe('ADD_ITEM', () => {
+      const value = 'Item one';
+      const label = 'Item one';
+      const id = 1234;
+      const choiceId = 5678;
+      const groupId = 1;
+      const customProperties = {
+        property: 'value',
+      };
+      const placeholder = true;
+      const keyCode = 10;
+
+      describe('passing expected values', () => {
+        let actualResponse;
+
+        beforeEach(() => {
+          actualResponse = items(undefined, {
+            type: 'ADD_ITEM',
+            value,
+            label,
+            id,
+            choiceId,
+            groupId,
+            customProperties,
+            placeholder,
+            keyCode,
+          });
+        });
+
+        it('adds item', () => {
+          const expectedResponse = [
+            {
+              id,
+              choiceId,
+              groupId,
+              value,
+              label,
+              active: true,
+              highlighted: false,
+              customProperties,
+              placeholder,
+              keyCode: null,
+            },
+          ];
+
+          expect(actualResponse).to.eql(expectedResponse);
+        });
+
+        it('unhighlights all highlighted items', () => {
+          actualResponse.forEach((item) => {
+            expect(item.highlighted).to.equal(false);
+          });
+        });
+      });
+
+      describe('fallback values', () => {
+        describe('passing no placeholder value', () => {
+          it('adds item with placeholder set to false', () => {
+            const expectedResponse = [
+              {
+                id,
+                choiceId,
+                groupId,
+                value,
+                label,
+                active: true,
+                highlighted: false,
+                customProperties,
+                placeholder: false,
+                keyCode: null,
+              },
+            ];
+
+            const actualResponse = items(undefined, {
+              type: 'ADD_ITEM',
+              value,
+              label,
+              id,
+              choiceId,
+              groupId,
+              customProperties,
+              placeholder: undefined,
+              keyCode,
+            });
+
+            expect(actualResponse).to.eql(expectedResponse);
+          });
+        });
+      });
+    });
+  });
+
+  describe('when items exist', () => {
+    let state;
+
+    beforeEach(() => {
+      state = [
+        {
+          id: 1,
+          choiceId: 1,
+          groupId: -1,
+          value: 'Item one',
+          label: 'Item one',
+          active: false,
+          highlighted: false,
+          customProperties: null,
+          placeholder: false,
+          keyCode: null,
+        },
+        {
+          id: 2,
+          choiceId: 2,
+          groupId: -1,
+          value: 'Item one',
+          label: 'Item one',
+          active: true,
+          highlighted: false,
+          customProperties: null,
+          placeholder: false,
+          keyCode: null,
+        },
+      ];
+    });
+
+    describe('REMOVE_ITEM', () => {
+      it('sets an item to be inactive based on passed ID', () => {
+        const clonedState = state.slice(0);
+        const id = 2;
+        const expectedResponse = [
+          {
+            ...state[0],
+          },
+          {
+            ...state[1],
+            active: false,
+          },
+        ];
+
+        const actualResponse = items(clonedState, {
+          type: 'REMOVE_ITEM',
+          id,
+        } as RemoveItemAction);
+
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+    });
+
+    describe('HIGHLIGHT_ITEM', () => {
+      it('sets an item to be inactive based on passed ID', () => {
+        const clonedState = state.slice(0);
+        const id = 2;
+        const expectedResponse = [
+          {
+            ...state[0],
+          },
+          {
+            ...state[1],
+            highlighted: true,
+          },
+        ];
+
+        const actualResponse = items(clonedState, {
+          type: 'HIGHLIGHT_ITEM',
+          id,
+          highlighted: true,
+        });
+
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/items.ts b/my_flask_app/static/choices.js/src/scripts/reducers/items.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4c1ec8dfa941de679982a5d10f120baf923009c
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/items.ts
@@ -0,0 +1,78 @@
+import {
+  AddItemAction,
+  RemoveItemAction,
+  HighlightItemAction,
+} from '../actions/items';
+import { Item } from '../interfaces/item';
+import { State } from '../interfaces/state';
+
+export const defaultState = [];
+
+type ActionTypes =
+  | AddItemAction
+  | RemoveItemAction
+  | HighlightItemAction
+  | Record<string, never>;
+
+export default function items(
+  state: Item[] = defaultState,
+  action: ActionTypes = {},
+): State['items'] {
+  switch (action.type) {
+    case 'ADD_ITEM': {
+      const addItemAction = action as AddItemAction;
+      // Add object to items array
+      const newState = [
+        ...state,
+        {
+          id: addItemAction.id,
+          choiceId: addItemAction.choiceId,
+          groupId: addItemAction.groupId,
+          value: addItemAction.value,
+          label: addItemAction.label,
+          active: true,
+          highlighted: false,
+          customProperties: addItemAction.customProperties,
+          placeholder: addItemAction.placeholder || false,
+          keyCode: null,
+        },
+      ];
+
+      return newState.map((obj: Item) => {
+        const item = obj;
+        item.highlighted = false;
+
+        return item;
+      });
+    }
+
+    case 'REMOVE_ITEM': {
+      // Set item to inactive
+      return state.map((obj) => {
+        const item = obj;
+        if (item.id === action.id) {
+          item.active = false;
+        }
+
+        return item;
+      });
+    }
+
+    case 'HIGHLIGHT_ITEM': {
+      const highlightItemAction = action as HighlightItemAction;
+
+      return state.map((obj) => {
+        const item = obj;
+        if (item.id === highlightItemAction.id) {
+          item.highlighted = highlightItemAction.highlighted;
+        }
+
+        return item;
+      });
+    }
+
+    default: {
+      return state;
+    }
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/loading.test.ts b/my_flask_app/static/choices.js/src/scripts/reducers/loading.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cbf9c4bf056fb6437096dd96f754a8a026d32d5d
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/loading.test.ts
@@ -0,0 +1,21 @@
+import { expect } from 'chai';
+import general, { defaultState } from './loading';
+
+describe('reducers/loading', () => {
+  it('should return same state when no action matches', () => {
+    expect(general(defaultState, {} as any)).to.equal(defaultState);
+  });
+
+  describe('SET_IS_LOADING', () => {
+    it('sets loading state', () => {
+      const expectedState = true;
+
+      const actualState = general(undefined, {
+        type: 'SET_IS_LOADING',
+        isLoading: true,
+      });
+
+      expect(expectedState).to.eql(actualState);
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/reducers/loading.ts b/my_flask_app/static/choices.js/src/scripts/reducers/loading.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd54b52f4ab61c228a37eb756d779f6ca670610e
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/reducers/loading.ts
@@ -0,0 +1,23 @@
+import { SetIsLoadingAction } from '../actions/misc';
+import { State } from '../interfaces/state';
+
+export const defaultState = false;
+
+type ActionTypes = SetIsLoadingAction | Record<string, never>;
+
+const general = (
+  state = defaultState,
+  action: ActionTypes = {},
+): State['loading'] => {
+  switch (action.type) {
+    case 'SET_IS_LOADING': {
+      return action.isLoading;
+    }
+
+    default: {
+      return state;
+    }
+  }
+};
+
+export default general;
diff --git a/my_flask_app/static/choices.js/src/scripts/store/store.test.ts b/my_flask_app/static/choices.js/src/scripts/store/store.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e7be703e07c3f98ac81c9048a55d0fd59c214c04
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/store/store.test.ts
@@ -0,0 +1,257 @@
+import { expect } from 'chai';
+import sinon from 'sinon';
+import Store from './store';
+
+describe('reducers/store', () => {
+  let instance;
+  let subscribeStub;
+  let dispatchStub;
+  let getStateStub;
+
+  beforeEach(() => {
+    instance = new Store();
+    subscribeStub = sinon.stub(instance._store, 'subscribe');
+    dispatchStub = sinon.stub(instance._store, 'dispatch');
+    getStateStub = sinon.stub(instance._store, 'getState');
+  });
+
+  afterEach(() => {
+    subscribeStub.restore();
+    dispatchStub.restore();
+    getStateStub.restore();
+  });
+
+  describe('constructor', () => {
+    it('creates redux store', () => {
+      expect(instance._store).to.contain.keys([
+        'subscribe',
+        'dispatch',
+        'getState',
+      ]);
+    });
+  });
+
+  describe('subscribe', () => {
+    it('wraps redux subscribe method', () => {
+      const onChange = (): void => {};
+      expect(subscribeStub.callCount).to.equal(0);
+      instance.subscribe(onChange);
+      expect(subscribeStub.callCount).to.equal(1);
+      expect(subscribeStub.firstCall.args[0]).to.equal(onChange);
+    });
+  });
+
+  describe('dispatch', () => {
+    it('wraps redux dispatch method', () => {
+      const action = 'TEST_ACTION';
+      expect(dispatchStub.callCount).to.equal(0);
+      instance.dispatch(action);
+      expect(dispatchStub.callCount).to.equal(1);
+      expect(dispatchStub.firstCall.args[0]).to.equal(action);
+    });
+  });
+
+  describe('state getter', () => {
+    it('returns state', () => {
+      const state = { items: [] };
+      getStateStub.returns(state);
+
+      expect(instance.state).to.equal(state);
+    });
+  });
+
+  describe('store selectors', () => {
+    let state;
+
+    beforeEach(() => {
+      state = {
+        items: [
+          {
+            id: 1,
+            choiceId: 1,
+            groupId: -1,
+            value: 'Item one',
+            label: 'Item one',
+            active: false,
+            highlighted: false,
+            customProperties: null,
+            placeholder: false,
+            keyCode: null,
+          },
+          {
+            id: 2,
+            choiceId: 2,
+            groupId: -1,
+            value: 'Item two',
+            label: 'Item two',
+            active: true,
+            highlighted: false,
+            customProperties: null,
+            placeholder: false,
+            keyCode: null,
+          },
+          {
+            id: 3,
+            choiceId: 3,
+            groupId: -1,
+            value: 'Item three',
+            label: 'Item three',
+            active: true,
+            highlighted: true,
+            customProperties: null,
+            placeholder: false,
+            keyCode: null,
+          },
+        ],
+        choices: [
+          {
+            id: 1,
+            elementId: 'choices-test-1',
+            groupId: -1,
+            value: 'Choice 1',
+            label: 'Choice 1',
+            disabled: false,
+            selected: false,
+            active: true,
+            score: 9999,
+            customProperties: null,
+            placeholder: false,
+            keyCode: null,
+          },
+          {
+            id: 2,
+            elementId: 'choices-test-2',
+            groupId: -1,
+            value: 'Choice 2',
+            label: 'Choice 2',
+            disabled: false,
+            selected: true,
+            active: false,
+            score: 9999,
+            customProperties: null,
+            placeholder: false,
+            keyCode: null,
+          },
+        ],
+        groups: [
+          {
+            id: 1,
+            value: 'Group one',
+            active: true,
+            disabled: false,
+          },
+          {
+            id: 2,
+            value: 'Group two',
+            active: true,
+            disabled: false,
+          },
+        ],
+      };
+
+      getStateStub.returns(state);
+    });
+
+    describe('items getter', () => {
+      it('returns items', () => {
+        const expectedResponse = state.items;
+        expect(instance.items).to.eql(expectedResponse);
+      });
+    });
+
+    describe('activeItems getter', () => {
+      it('returns items that are active', () => {
+        const expectedResponse = state.items.filter((item) => item.active);
+        expect(instance.activeItems).to.eql(expectedResponse);
+      });
+    });
+
+    describe('highlightedActiveItems getter', () => {
+      it('returns items that are active and highlighted', () => {
+        const expectedResponse = state.items.filter(
+          (item) => item.highlighted && item.active,
+        );
+        expect(instance.highlightedActiveItems).to.eql(expectedResponse);
+      });
+    });
+
+    describe('choices getter', () => {
+      it('returns choices', () => {
+        const expectedResponse = state.choices;
+        expect(instance.choices).to.eql(expectedResponse);
+      });
+    });
+
+    describe('activeChoices getter', () => {
+      it('returns choices that are active', () => {
+        const expectedResponse = state.choices.filter(
+          (choice) => choice.active,
+        );
+        expect(instance.activeChoices).to.eql(expectedResponse);
+      });
+    });
+
+    describe('selectableChoices getter', () => {
+      it('returns choices that are not disabled', () => {
+        const expectedResponse = state.choices.filter(
+          (choice) => !choice.disabled,
+        );
+        expect(instance.selectableChoices).to.eql(expectedResponse);
+      });
+    });
+
+    describe('searchableChoices getter', () => {
+      it('returns choices that are not placeholders and are selectable', () => {
+        const expectedResponse = state.choices.filter(
+          (choice) => !choice.disabled && !choice.placeholder,
+        );
+        expect(instance.searchableChoices).to.eql(expectedResponse);
+      });
+    });
+
+    describe('getChoiceById', () => {
+      describe('passing id', () => {
+        it('returns active choice by passed id', () => {
+          const id = '1';
+          const expectedResponse = state.choices.find(
+            (choice) => choice.id === parseInt(id, 10),
+          );
+          const actualResponse = instance.getChoiceById(id);
+          expect(actualResponse).to.eql(expectedResponse);
+        });
+      });
+    });
+
+    describe('placeholderChoice getter', () => {
+      it('returns placeholder choice', () => {
+        const expectedResponse = state.choices
+          .reverse()
+          .find((choice) => choice.placeholder);
+        expect(instance.getPlaceholderChoice).to.eql(expectedResponse);
+      });
+    });
+
+    describe('groups getter', () => {
+      it('returns groups', () => {
+        const expectedResponse = state.groups;
+        expect(instance.groups).to.eql(expectedResponse);
+      });
+    });
+
+    describe('activeGroups getter', () => {
+      it('returns active groups', () => {
+        const expectedResponse = state.groups.filter((group) => group.active);
+        expect(instance.activeGroups).to.eql(expectedResponse);
+      });
+    });
+
+    describe('getGroupById', () => {
+      it('returns group by id', () => {
+        const id = 1;
+        const expectedResponse = state.groups.find((group) => group.id === id);
+        const actualResponse = instance.getGroupById(id);
+        expect(actualResponse).to.eql(expectedResponse);
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/store/store.ts b/my_flask_app/static/choices.js/src/scripts/store/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eaa20fd5fa0ab242b8d3c009af37e235961a047c
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/store/store.ts
@@ -0,0 +1,144 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { createStore, Store as IStore, AnyAction } from 'redux';
+import { Choice } from '../interfaces/choice';
+import { Group } from '../interfaces/group';
+import { Item } from '../interfaces/item';
+import { State } from '../interfaces/state';
+import rootReducer from '../reducers/index';
+
+export default class Store {
+  _store: IStore;
+
+  constructor() {
+    this._store = createStore(
+      rootReducer,
+      (window as any).__REDUX_DEVTOOLS_EXTENSION__ &&
+        (window as any).__REDUX_DEVTOOLS_EXTENSION__(),
+    );
+  }
+
+  /**
+   * Subscribe store to function call (wrapped Redux method)
+   */
+  subscribe(onChange: () => void): void {
+    this._store.subscribe(onChange);
+  }
+
+  /**
+   * Dispatch event to store (wrapped Redux method)
+   */
+  dispatch(action: AnyAction): void {
+    this._store.dispatch(action);
+  }
+
+  /**
+   * Get store object (wrapping Redux method)
+   */
+  get state(): State {
+    return this._store.getState();
+  }
+
+  /**
+   * Get items from store
+   */
+  get items(): Item[] {
+    return this.state.items;
+  }
+
+  /**
+   * Get active items from store
+   */
+  get activeItems(): Item[] {
+    return this.items.filter((item) => item.active === true);
+  }
+
+  /**
+   * Get highlighted items from store
+   */
+  get highlightedActiveItems(): Item[] {
+    return this.items.filter((item) => item.active && item.highlighted);
+  }
+
+  /**
+   * Get choices from store
+   */
+  get choices(): Choice[] {
+    return this.state.choices;
+  }
+
+  /**
+   * Get active choices from store
+   */
+  get activeChoices(): Choice[] {
+    return this.choices.filter((choice) => choice.active === true);
+  }
+
+  /**
+   * Get selectable choices from store
+   */
+  get selectableChoices(): Choice[] {
+    return this.choices.filter((choice) => choice.disabled !== true);
+  }
+
+  /**
+   * Get choices that can be searched (excluding placeholders)
+   */
+  get searchableChoices(): Choice[] {
+    return this.selectableChoices.filter(
+      (choice) => choice.placeholder !== true,
+    );
+  }
+
+  /**
+   * Get placeholder choice from store
+   */
+  get placeholderChoice(): Choice | undefined {
+    return [...this.choices]
+      .reverse()
+      .find((choice) => choice.placeholder === true);
+  }
+
+  /**
+   * Get groups from store
+   */
+  get groups(): Group[] {
+    return this.state.groups;
+  }
+
+  /**
+   * Get active groups from store
+   */
+  get activeGroups(): Group[] {
+    const { groups, choices } = this;
+
+    return groups.filter((group) => {
+      const isActive = group.active === true && group.disabled === false;
+      const hasActiveOptions = choices.some(
+        (choice) => choice.active === true && choice.disabled === false,
+      );
+
+      return isActive && hasActiveOptions;
+    }, []);
+  }
+
+  /**
+   * Get loading state from store
+   */
+  isLoading(): boolean {
+    return this.state.loading;
+  }
+
+  /**
+   * Get single choice by it's ID
+   */
+  getChoiceById(id: string): Choice | undefined {
+    return this.activeChoices.find((choice) => choice.id === parseInt(id, 10));
+  }
+
+  /**
+   * Get group by group id
+   */
+  getGroupById(id: number): Group | undefined {
+    return this.groups.find((group) => group.id === id);
+  }
+}
diff --git a/my_flask_app/static/choices.js/src/scripts/templates.test.ts b/my_flask_app/static/choices.js/src/scripts/templates.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4af46976550aa3a46e00f8218ac5dccd03c6f68
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/templates.test.ts
@@ -0,0 +1,716 @@
+import { expect } from 'chai';
+import templates from './templates';
+import { strToEl } from './lib/utils';
+import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from './defaults';
+import { Options } from './interfaces/options';
+import { ClassNames } from './interfaces/class-names';
+
+/**
+ * @param {HTMLElement} element1
+ * @param {HTMLElement} element2
+ */
+function expectEqualElements(element1, element2): void {
+  expect(element1.tagName).to.equal(element2.tagName);
+  expect(element1.attributes.length).to.equal(element2.attributes.length);
+  expect(Object.keys(element1.dataset)).to.have.members(
+    Object.keys(element2.dataset),
+  );
+  expect(element1.classList).to.include(element2.classList);
+  // compare attributes values
+  for (const attribute of Object.values(element1.attributes)) {
+    expect(element1.getAttribute(attribute)).to.equal(
+      element2.getAttribute(attribute),
+    );
+  }
+}
+
+function createOptionsWithPartialClasses(
+  classNames: Partial<ClassNames>,
+  options: Partial<Options> = {},
+): Options {
+  return {
+    ...DEFAULT_CONFIG,
+    ...options,
+    classNames: {
+      ...DEFAULT_CLASSNAMES,
+      ...classNames,
+    },
+  };
+}
+
+describe('templates', () => {
+  describe('containerOuter', () => {
+    const options = createOptionsWithPartialClasses({
+      containerOuter: 'class-1',
+    });
+    const direction = 'rtl';
+
+    describe('select element', () => {
+      describe('search enabled', () => {
+        it('returns expected html', () => {
+          const isSelectElement = true;
+          const isSelectOneElement = false;
+          const searchEnabled = true;
+          const passedElementType = 'select-multiple';
+          const labelId = '';
+
+          const expectedOutput = strToEl(`
+            <div
+              class="${options.classNames.containerOuter}"
+              data-type="${passedElementType}"
+              role="combobox"
+              aria-autocomplete="list"
+              aria-haspopup="true"
+              aria-expanded="false"
+              dir="${direction}"
+              >
+            </div>
+          `);
+          const actualOutput = templates.containerOuter(
+            options,
+            direction,
+            isSelectElement,
+            isSelectOneElement,
+            searchEnabled,
+            passedElementType,
+            labelId,
+          );
+          expectEqualElements(actualOutput, expectedOutput);
+        });
+      });
+
+      describe('with label id for a11y', () => {
+        it('returns expected html', () => {
+          const isSelectElement = true;
+          const isSelectOneElement = true;
+          const searchEnabled = false;
+          const passedElementType = 'select-one';
+          const labelId = 'testLabelId';
+
+          const expectedOutput = strToEl(`
+            <div
+              class="${options.classNames.containerOuter}"
+              data-type="${passedElementType}"
+              role="listbox"
+              tabindex="0"
+              aria-haspopup="true"
+              aria-expanded="false"
+              aria-labelledby="${labelId}"
+              dir="${direction}"
+              >
+            </div>
+          `);
+          const actualOutput = templates.containerOuter(
+            options,
+            direction,
+            isSelectElement,
+            isSelectOneElement,
+            searchEnabled,
+            passedElementType,
+            labelId,
+          );
+          expectEqualElements(actualOutput, expectedOutput);
+        });
+      });
+
+      describe('search disabled', () => {
+        it('returns expected html', () => {
+          const isSelectElement = true;
+          const isSelectOneElement = false;
+          const searchEnabled = false;
+          const passedElementType = 'select-multiple';
+          const labelId = '';
+
+          const expectedOutput = strToEl(`
+            <div
+              class="${options.classNames.containerOuter}"
+              data-type="${passedElementType}"
+              role="listbox"
+              aria-haspopup="true"
+              aria-expanded="false"
+              dir="${direction}"
+              >
+            </div>
+          `);
+          const actualOutput = templates.containerOuter(
+            options,
+            direction,
+            isSelectElement,
+            isSelectOneElement,
+            searchEnabled,
+            passedElementType,
+            labelId,
+          );
+
+          expectEqualElements(actualOutput, expectedOutput);
+        });
+      });
+
+      describe('select one element', () => {
+        it('returns expected html', () => {
+          const isSelectElement = true;
+          const isSelectOneElement = true;
+          const searchEnabled = false;
+          const passedElementType = 'select-one';
+          const labelId = '';
+
+          const expectedOutput = strToEl(`
+            <div
+              class="${options.classNames.containerOuter}"
+              data-type="${passedElementType}"
+              role="listbox"
+              tabindex="0"
+              aria-haspopup="true"
+              aria-expanded="false"
+              dir="${direction}"
+              >
+            </div>
+          `);
+          const actualOutput = templates.containerOuter(
+            options,
+            direction,
+            isSelectElement,
+            isSelectOneElement,
+            searchEnabled,
+            passedElementType,
+            labelId,
+          );
+
+          expectEqualElements(actualOutput, expectedOutput);
+        });
+      });
+    });
+
+    describe('non select element', () => {
+      it('returns expected html', () => {
+        const isSelectElement = false;
+        const isSelectOneElement = false;
+        const searchEnabled = false;
+        const passedElementType = 'text';
+        const labelId = '';
+
+        const expectedOutput = strToEl(`
+          <div
+            class="${options.classNames.containerOuter}"
+            data-type="${passedElementType}"
+            aria-haspopup="true"
+            aria-expanded="false"
+            dir="${direction}"
+            >
+          </div>
+        `);
+        const actualOutput = templates.containerOuter(
+          options,
+          direction,
+          isSelectElement,
+          isSelectOneElement,
+          searchEnabled,
+          passedElementType,
+          labelId,
+        );
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+  });
+
+  describe('containerInner', () => {
+    it('returns expected html', () => {
+      const innerOptions = createOptionsWithPartialClasses({
+        containerInner: 'class-1',
+      });
+      const expectedOutput = strToEl(
+        `<div class="${innerOptions.classNames.containerInner}"></div>`,
+      );
+      const actualOutput = templates.containerInner(innerOptions);
+
+      expectEqualElements(actualOutput, expectedOutput);
+    });
+  });
+
+  describe('itemList', () => {
+    const itemOptions = createOptionsWithPartialClasses({
+      list: 'class-1',
+      listSingle: 'class-2',
+      listItems: 'class-3',
+    });
+
+    describe('select one element', () => {
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(
+          `<div class="${itemOptions.classNames.list} ${itemOptions.classNames.listSingle}"></div>`,
+        );
+        const actualOutput = templates.itemList(itemOptions, true);
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+
+    describe('non select one element', () => {
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(
+          `<div class="${itemOptions.classNames.list} ${itemOptions.classNames.listItems}"></div>`,
+        );
+        const actualOutput = templates.itemList(itemOptions, false);
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+  });
+
+  describe('placeholder', () => {
+    it('returns expected html', () => {
+      const placeholderOptions = createOptionsWithPartialClasses({
+        placeholder: 'class-1',
+      });
+      const value = 'test';
+      const expectedOutput = strToEl(`
+        <div class="${placeholderOptions.classNames.placeholder}">${value}</div>`);
+      const actualOutput = templates.placeholder(placeholderOptions, value);
+
+      expectEqualElements(actualOutput, expectedOutput);
+    });
+  });
+
+  describe('choiceList', () => {
+    const choiceListOptions = createOptionsWithPartialClasses({
+      list: 'class-1',
+    });
+
+    describe('select one element', () => {
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+            class="${choiceListOptions.classNames.list}"
+            role="listbox"
+            >
+          </div>
+        `);
+        const actualOutput = templates.choiceList(choiceListOptions, true);
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+
+    describe('non select one element', () => {
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+            class="${choiceListOptions.classNames.list}"
+            role="listbox"
+            aria-multiselectable="true"
+            >
+          </div>
+        `);
+        const actualOutput = templates.choiceList(choiceListOptions, false);
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+  });
+
+  describe('choiceGroup', () => {
+    const groupOptions = createOptionsWithPartialClasses({
+      group: 'class-1',
+      groupHeading: 'class-2',
+      itemDisabled: 'class-3',
+    });
+
+    let data;
+
+    beforeEach(() => {
+      data = {
+        id: 1,
+        value: 'test',
+        disabled: false,
+      };
+    });
+
+    describe('enabled state', () => {
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+          class="${groupOptions.classNames.group}"
+            data-group
+            data-id="${data.id}"
+            data-value="${data.value}"
+            role="group"
+            >
+            <div class="${groupOptions.classNames.groupHeading}">${data.value}</div>
+          </div>
+        `);
+        const actualOutput = templates.choiceGroup(groupOptions, data);
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+
+    describe('disabled state', () => {
+      beforeEach(() => {
+        data = {
+          ...data,
+          disabled: true,
+        };
+      });
+
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+            class="${groupOptions.classNames.group} ${groupOptions.classNames.itemDisabled}"
+            data-group
+            data-id="${data.id}"
+            data-value="${data.value}"
+            role="group"
+            aria-disabled="true"
+            >
+            <div class="${groupOptions.classNames.groupHeading}">${data.value}</div>
+          </div>
+        `);
+        const actualOutput = templates.choiceGroup(groupOptions, data);
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+  });
+
+  describe('choice', () => {
+    const choiceOptions = createOptionsWithPartialClasses({
+      item: 'class-1',
+      itemChoice: 'class-2',
+      itemDisabled: 'class-3',
+      itemSelectable: 'class-4',
+      placeholder: 'class-5',
+      selectedState: 'class-6',
+    });
+
+    const itemSelectText = 'test 6';
+
+    let data;
+
+    beforeEach(() => {
+      data = {
+        id: 1,
+        groupId: -1,
+        disabled: false,
+        elementId: 'test',
+        label: 'test',
+        value: 'test',
+        selected: false,
+      };
+    });
+
+    describe('enabled state', () => {
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+            class="${choiceOptions.classNames.item} ${choiceOptions.classNames.itemChoice} ${choiceOptions.classNames.itemSelectable}"
+            data-select-text="${itemSelectText}"
+            data-choice
+            data-id="${data.id}"
+            data-value="${data.value}"
+            data-choice-selectable
+            id="${data.elementId}"
+            role="option"
+            >
+            ${data.label}
+          </div>
+        `);
+        const actualOutput = templates.choice(
+          choiceOptions,
+          data,
+          itemSelectText,
+        );
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+
+    describe('disabled state', () => {
+      beforeEach(() => {
+        data = {
+          ...data,
+          disabled: true,
+        };
+      });
+
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+            class="${choiceOptions.classNames.item} ${choiceOptions.classNames.itemChoice} ${choiceOptions.classNames.itemDisabled}"
+            data-select-text="${itemSelectText}"
+            data-choice
+            data-id="${data.id}"
+            data-value="${data.value}"
+            data-choice-disabled
+            aria-disabled="true"
+            id="${data.elementId}"
+            role="option"
+            >
+            ${data.label}
+          </div>
+        `);
+        const actualOutput = templates.choice(
+          choiceOptions,
+          data,
+          itemSelectText,
+        );
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+
+    describe('selected state', () => {
+      beforeEach(() => {
+        data = {
+          ...data,
+          selected: true,
+        };
+      });
+
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+            class="${choiceOptions.classNames.item} ${choiceOptions.classNames.itemChoice} ${choiceOptions.classNames.selectedState} ${choiceOptions.classNames.itemSelectable}"
+            data-select-text="${itemSelectText}"
+            data-choice
+            data-id="${data.id}"
+            data-value="${data.value}"
+            data-choice-selectable
+            id="${data.elementId}"
+            role="option"
+            >
+            ${data.label}
+          </div>
+        `);
+        const actualOutput = templates.choice(
+          choiceOptions,
+          data,
+          itemSelectText,
+        );
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+
+    describe('placeholder', () => {
+      beforeEach(() => {
+        data = {
+          ...data,
+          placeholder: true,
+        };
+      });
+
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+            class="${choiceOptions.classNames.item} ${choiceOptions.classNames.itemChoice} ${choiceOptions.classNames.placeholder} ${choiceOptions.classNames.itemSelectable}"
+            data-select-text="${itemSelectText}"
+            data-choice
+            data-id="${data.id}"
+            data-value="${data.value}"
+            data-choice-selectable
+            id="${data.elementId}"
+            role="option"
+            >
+            ${data.label}
+          </div>
+        `);
+        const actualOutput = templates.choice(
+          choiceOptions,
+          data,
+          itemSelectText,
+        );
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+
+    describe('child of group', () => {
+      beforeEach(() => {
+        data = {
+          ...data,
+          groupId: 1,
+        };
+      });
+
+      it('returns expected html', () => {
+        const expectedOutput = strToEl(`
+          <div
+            class="${choiceOptions.classNames.item} ${choiceOptions.classNames.itemChoice} ${choiceOptions.classNames.itemSelectable}"
+            data-select-text="${itemSelectText}"
+            data-choice
+            data-id="${data.id}"
+            data-value="${data.value}"
+            data-choice-selectable
+            id="${data.elementId}"
+            role="treeitem"
+            >
+            ${data.label}
+          </div>
+        `);
+        const actualOutput = templates.choice(
+          choiceOptions,
+          data,
+          itemSelectText,
+        );
+
+        expectEqualElements(actualOutput, expectedOutput);
+      });
+    });
+  });
+
+  describe('input', () => {
+    const inputOptions = createOptionsWithPartialClasses({
+      input: 'class-1',
+      inputCloned: 'class-2',
+    });
+
+    it('returns expected html', () => {
+      /*
+        Following attributes are not supported by JSDOM, so, can't compare
+          autocapitalize="off"
+          spellcheck="false"
+      */
+      const expectedOutput = strToEl(`
+        <input
+          type="search"
+          name="search_terms"
+          class="${inputOptions.classNames.input} ${inputOptions.classNames.inputCloned}"
+          autocomplete="off"
+          role="textbox"
+          aria-autocomplete="list"
+          aria-label="test placeholder"
+        >
+      `);
+      const actualOutput = templates.input(inputOptions, 'test placeholder');
+
+      expectEqualElements(actualOutput, expectedOutput);
+    });
+  });
+
+  describe('dropdown', () => {
+    const dropdownOptions = createOptionsWithPartialClasses({
+      list: 'class-1',
+      listDropdown: 'class-2',
+    });
+
+    it('returns expected html', () => {
+      const expectedOutput = strToEl(
+        `<div class="${dropdownOptions.classNames.list} ${dropdownOptions.classNames.listDropdown}" aria-expanded="false"></div>`,
+      );
+      const actualOutput = templates.dropdown(dropdownOptions);
+
+      expectEqualElements(actualOutput, expectedOutput);
+    });
+  });
+
+  describe('notice', () => {
+    const noticeOptions = createOptionsWithPartialClasses({
+      item: 'class-1',
+      itemChoice: 'class-2',
+      noResults: 'class-3',
+      noChoices: 'class-4',
+    });
+
+    const label = 'test';
+
+    it('returns expected html', () => {
+      const expectedOutput = strToEl(`
+        <div class="${noticeOptions.classNames.item} ${noticeOptions.classNames.itemChoice}">
+          ${label}
+        </div>
+      `);
+      const actualOutput = templates.notice(noticeOptions, label);
+
+      expectEqualElements(actualOutput, expectedOutput);
+    });
+
+    describe('passing a notice type', () => {
+      describe('no results', () => {
+        it('adds no results classname', () => {
+          const expectedOutput = strToEl(`
+            <div class="${noticeOptions.classNames.item} ${noticeOptions.classNames.itemChoice} ${noticeOptions.classNames.noResults}">
+              ${label}
+            </div>
+          `);
+          const actualOutput = templates.notice(
+            noticeOptions,
+            label,
+            'no-results',
+          );
+
+          expectEqualElements(actualOutput, expectedOutput);
+        });
+      });
+
+      describe('no choices', () => {
+        it('adds no choices classname', () => {
+          const expectedOutput = strToEl(`
+            <div class="${noticeOptions.classNames.item} ${noticeOptions.classNames.itemChoice} ${noticeOptions.classNames.noChoices}">
+              ${label}
+            </div>
+          `);
+          const actualOutput = templates.notice(
+            noticeOptions,
+            label,
+            'no-choices',
+          );
+
+          expectEqualElements(actualOutput, expectedOutput);
+        });
+      });
+    });
+  });
+
+  describe('option', () => {
+    let data;
+
+    beforeEach(() => {
+      data = {
+        disabled: false,
+        selected: false,
+        value: 'test value',
+        label: 'test label',
+      };
+    });
+
+    it('returns expected html', () => {
+      const expectedOutput = strToEl(
+        `<option value="${data.value}" ${data.selected ? 'selected' : ''} ${
+          data.disabled ? 'disabled' : ''
+        }>${data.label}</option>`,
+      );
+      const actualOutput = templates.option(data);
+
+      expectEqualElements(actualOutput, expectedOutput);
+    });
+
+    describe('when selected', () => {
+      beforeEach(() => {
+        data = {
+          ...data,
+          active: true,
+        };
+      });
+
+      it('sets selected attr to true', () => {
+        const output = templates.option(data);
+        expect(output.selected).to.equal(true);
+      });
+    });
+
+    describe('when disabled', () => {
+      beforeEach(() => {
+        data = {
+          ...data,
+          disabled: true,
+        };
+      });
+
+      it('sets disabled attr to true', () => {
+        const output = templates.option(data);
+        expect(output.disabled).to.equal(true);
+      });
+    });
+  });
+});
diff --git a/my_flask_app/static/choices.js/src/scripts/templates.ts b/my_flask_app/static/choices.js/src/scripts/templates.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee4eafd612b8d72ea1ea8a0ab3268117fc7827e5
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/scripts/templates.ts
@@ -0,0 +1,333 @@
+/**
+ * Helpers to create HTML elements used by Choices
+ * Can be overridden by providing `callbackOnCreateTemplates` option
+ */
+
+import { Choice } from './interfaces/choice';
+import { Group } from './interfaces/group';
+import { Item } from './interfaces/item';
+import { PassedElementType } from './interfaces/passed-element-type';
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type TemplateOptions = Record<'classNames' | 'allowHTML', any>;
+
+const templates = {
+  containerOuter(
+    { classNames: { containerOuter } }: TemplateOptions,
+    dir: HTMLElement['dir'],
+    isSelectElement: boolean,
+    isSelectOneElement: boolean,
+    searchEnabled: boolean,
+    passedElementType: PassedElementType,
+    labelId: string,
+  ): HTMLDivElement {
+    const div = Object.assign(document.createElement('div'), {
+      className: containerOuter,
+    });
+
+    div.dataset.type = passedElementType;
+
+    if (dir) {
+      div.dir = dir;
+    }
+
+    if (isSelectOneElement) {
+      div.tabIndex = 0;
+    }
+
+    if (isSelectElement) {
+      div.setAttribute('role', searchEnabled ? 'combobox' : 'listbox');
+      if (searchEnabled) {
+        div.setAttribute('aria-autocomplete', 'list');
+      }
+    }
+
+    div.setAttribute('aria-haspopup', 'true');
+    div.setAttribute('aria-expanded', 'false');
+    if (labelId) {
+      div.setAttribute('aria-labelledby', labelId);
+    }
+
+    return div;
+  },
+
+  containerInner({
+    classNames: { containerInner },
+  }: TemplateOptions): HTMLDivElement {
+    return Object.assign(document.createElement('div'), {
+      className: containerInner,
+    });
+  },
+
+  itemList(
+    { classNames: { list, listSingle, listItems } }: TemplateOptions,
+    isSelectOneElement: boolean,
+  ): HTMLDivElement {
+    return Object.assign(document.createElement('div'), {
+      className: `${list} ${isSelectOneElement ? listSingle : listItems}`,
+    });
+  },
+
+  placeholder(
+    { allowHTML, classNames: { placeholder } }: TemplateOptions,
+    value: string,
+  ): HTMLDivElement {
+    return Object.assign(document.createElement('div'), {
+      className: placeholder,
+      [allowHTML ? 'innerHTML' : 'innerText']: value,
+    });
+  },
+
+  item(
+    {
+      allowHTML,
+      classNames: {
+        item,
+        button,
+        highlightedState,
+        itemSelectable,
+        placeholder,
+      },
+    }: TemplateOptions,
+    {
+      id,
+      value,
+      label,
+      customProperties,
+      active,
+      disabled,
+      highlighted,
+      placeholder: isPlaceholder,
+    }: Item,
+    removeItemButton: boolean,
+  ): HTMLDivElement {
+    const div = Object.assign(document.createElement('div'), {
+      className: item,
+      [allowHTML ? 'innerHTML' : 'innerText']: label,
+    });
+
+    Object.assign(div.dataset, {
+      item: '',
+      id,
+      value,
+      customProperties,
+    });
+
+    if (active) {
+      div.setAttribute('aria-selected', 'true');
+    }
+
+    if (disabled) {
+      div.setAttribute('aria-disabled', 'true');
+    }
+
+    if (isPlaceholder) {
+      div.classList.add(placeholder);
+    }
+
+    div.classList.add(highlighted ? highlightedState : itemSelectable);
+
+    if (removeItemButton) {
+      if (disabled) {
+        div.classList.remove(itemSelectable);
+      }
+      div.dataset.deletable = '';
+      /** @todo This MUST be localizable, not hardcoded! */
+      const REMOVE_ITEM_TEXT = 'Remove item';
+      const removeButton = Object.assign(document.createElement('button'), {
+        type: 'button',
+        className: button,
+        [allowHTML ? 'innerHTML' : 'innerText']: REMOVE_ITEM_TEXT,
+      });
+      removeButton.setAttribute(
+        'aria-label',
+        `${REMOVE_ITEM_TEXT}: '${value}'`,
+      );
+      removeButton.dataset.button = '';
+      div.appendChild(removeButton);
+    }
+
+    return div;
+  },
+
+  choiceList(
+    { classNames: { list } }: TemplateOptions,
+    isSelectOneElement: boolean,
+  ): HTMLDivElement {
+    const div = Object.assign(document.createElement('div'), {
+      className: list,
+    });
+
+    if (!isSelectOneElement) {
+      div.setAttribute('aria-multiselectable', 'true');
+    }
+    div.setAttribute('role', 'listbox');
+
+    return div;
+  },
+
+  choiceGroup(
+    {
+      allowHTML,
+      classNames: { group, groupHeading, itemDisabled },
+    }: TemplateOptions,
+    { id, value, disabled }: Group,
+  ): HTMLDivElement {
+    const div = Object.assign(document.createElement('div'), {
+      className: `${group} ${disabled ? itemDisabled : ''}`,
+    });
+
+    div.setAttribute('role', 'group');
+
+    Object.assign(div.dataset, {
+      group: '',
+      id,
+      value,
+    });
+
+    if (disabled) {
+      div.setAttribute('aria-disabled', 'true');
+    }
+
+    div.appendChild(
+      Object.assign(document.createElement('div'), {
+        className: groupHeading,
+        [allowHTML ? 'innerHTML' : 'innerText']: value,
+      }),
+    );
+
+    return div;
+  },
+
+  choice(
+    {
+      allowHTML,
+      classNames: {
+        item,
+        itemChoice,
+        itemSelectable,
+        selectedState,
+        itemDisabled,
+        placeholder,
+      },
+    }: TemplateOptions,
+    {
+      id,
+      value,
+      label,
+      groupId,
+      elementId,
+      disabled: isDisabled,
+      selected: isSelected,
+      placeholder: isPlaceholder,
+    }: Choice,
+    selectText: string,
+  ): HTMLDivElement {
+    const div = Object.assign(document.createElement('div'), {
+      id: elementId,
+      [allowHTML ? 'innerHTML' : 'innerText']: label,
+      className: `${item} ${itemChoice}`,
+    });
+
+    if (isSelected) {
+      div.classList.add(selectedState);
+    }
+
+    if (isPlaceholder) {
+      div.classList.add(placeholder);
+    }
+
+    div.setAttribute('role', groupId && groupId > 0 ? 'treeitem' : 'option');
+
+    Object.assign(div.dataset, {
+      choice: '',
+      id,
+      value,
+      selectText,
+    });
+
+    if (isDisabled) {
+      div.classList.add(itemDisabled);
+      div.dataset.choiceDisabled = '';
+      div.setAttribute('aria-disabled', 'true');
+    } else {
+      div.classList.add(itemSelectable);
+      div.dataset.choiceSelectable = '';
+    }
+
+    return div;
+  },
+
+  input(
+    { classNames: { input, inputCloned } }: TemplateOptions,
+    placeholderValue: string,
+  ): HTMLInputElement {
+    const inp = Object.assign(document.createElement('input'), {
+      type: 'search',
+      name: 'search_terms',
+      className: `${input} ${inputCloned}`,
+      autocomplete: 'off',
+      autocapitalize: 'off',
+      spellcheck: false,
+    });
+
+    inp.setAttribute('role', 'textbox');
+    inp.setAttribute('aria-autocomplete', 'list');
+    inp.setAttribute('aria-label', placeholderValue);
+
+    return inp;
+  },
+
+  dropdown({
+    classNames: { list, listDropdown },
+  }: TemplateOptions): HTMLDivElement {
+    const div = document.createElement('div');
+
+    div.classList.add(list, listDropdown);
+    div.setAttribute('aria-expanded', 'false');
+
+    return div;
+  },
+
+  notice(
+    {
+      allowHTML,
+      classNames: { item, itemChoice, noResults, noChoices },
+    }: TemplateOptions,
+    innerText: string,
+    type: 'no-choices' | 'no-results' | '' = '',
+  ): HTMLDivElement {
+    const classes = [item, itemChoice];
+
+    if (type === 'no-choices') {
+      classes.push(noChoices);
+    } else if (type === 'no-results') {
+      classes.push(noResults);
+    }
+
+    return Object.assign(document.createElement('div'), {
+      [allowHTML ? 'innerHTML' : 'innerText']: innerText,
+      className: classes.join(' '),
+    });
+  },
+
+  option({
+    label,
+    value,
+    customProperties,
+    active,
+    disabled,
+  }: Item): HTMLOptionElement {
+    const opt = new Option(label, value, false, active);
+
+    if (customProperties) {
+      opt.dataset.customProperties = `${customProperties}`;
+    }
+
+    opt.disabled = !!disabled;
+
+    return opt;
+  },
+};
+
+export default templates;
diff --git a/my_flask_app/static/choices.js/src/styles/base.scss b/my_flask_app/static/choices.js/src/styles/base.scss
new file mode 100644
index 0000000000000000000000000000000000000000..00c2d47771b742d20771bd35fe65ad3f57d21f08
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/styles/base.scss
@@ -0,0 +1,189 @@
+/* =============================================
+=            Generic styling                  =
+============================================= */
+
+$global-guttering: 24px;
+$global-font-size-h1: 32px;
+$global-font-size-h2: 24px;
+$global-font-size-h3: 20px;
+$global-font-size-h4: 18px;
+$global-font-size-h5: 16px;
+$global-font-size-h6: 14px;
+
+* {
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+}
+
+html,
+body {
+  position: relative;
+  margin: 0;
+  width: 100%;
+  height: 100%;
+}
+
+body {
+  font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
+  font-size: 16px;
+  line-height: 1.4;
+  color: #fff;
+  background-color: #333;
+  overflow-x: hidden;
+}
+
+label {
+  display: block;
+  margin-bottom: 8px;
+  font-size: 14px;
+  font-weight: 500;
+  cursor: pointer;
+}
+
+p {
+  margin-top: 0;
+  margin-bottom: 8px;
+}
+
+hr {
+  display: block;
+  margin: $global-guttering * 1.25 0;
+  border: 0;
+  border-bottom: 1px solid #eaeaea;
+  height: 1px;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  margin-top: 0;
+  margin-bottom: $global-guttering * 0.5;
+  font-weight: 400;
+  line-height: 1.2;
+}
+
+a,
+a:visited,
+a:focus {
+  color: #fff;
+  text-decoration: none;
+  font-weight: 600;
+}
+
+.form-control {
+  display: block;
+  width: 100%;
+  background-color: #f9f9f9;
+  padding: 12px;
+  border: 1px solid #ddd;
+  border-radius: 2.5px;
+  font-size: 14px;
+  appearance: none;
+  margin-bottom: $global-guttering;
+}
+
+h1,
+.h1 {
+  font-size: $global-font-size-h1;
+}
+
+h2,
+.h2 {
+  font-size: $global-font-size-h2;
+}
+
+h3,
+.h3 {
+  font-size: $global-font-size-h3;
+}
+
+h4,
+.h4 {
+  font-size: $global-font-size-h4;
+}
+
+h5,
+.h5 {
+  font-size: $global-font-size-h5;
+}
+
+h6,
+.h6 {
+  font-size: $global-font-size-h6;
+}
+
+label + p {
+  margin-top: -4px;
+}
+
+.container {
+  display: block;
+  margin: auto;
+  max-width: 40em;
+  padding: $global-guttering * 2;
+
+  @media (max-width: 620px) {
+    padding: 0;
+  }
+}
+
+.section {
+  background-color: #fff;
+  padding: $global-guttering;
+  color: #333;
+
+  a,
+  a:visited,
+  a:focus {
+    color: #00bcd4;
+  }
+}
+
+.logo {
+  display: block;
+  margin-bottom: $global-guttering * 0.5;
+}
+
+.logo-img {
+  width: 100%;
+  height: auto;
+  display: inline-block;
+  max-width: 100%;
+  vertical-align: top;
+  padding: $global-guttering * 0.25 0;
+}
+
+.visible-ie {
+  display: none;
+}
+
+.push-bottom {
+  margin-bottom: $global-guttering;
+}
+
+.zero-bottom {
+  margin-bottom: 0;
+}
+
+.zero-top {
+  margin-top: 0;
+}
+
+.text-center {
+  text-align: center;
+}
+
+[data-test-hook] {
+  margin-bottom: $global-guttering;
+}
+
+/* =====  End of Section comment block  ====== */
diff --git a/my_flask_app/static/choices.js/src/styles/choices.scss b/my_flask_app/static/choices.js/src/styles/choices.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d9fc4346048c2ecda584dfb677ad46e2ce65d317
--- /dev/null
+++ b/my_flask_app/static/choices.js/src/styles/choices.scss
@@ -0,0 +1,413 @@
+/* ===============================
+=            Choices            =
+=============================== */
+
+@use "sass:color";
+
+$choices-selector: "choices" !default;
+$choices-font-size-lg: 16px !default;
+$choices-font-size-md: 14px !default;
+$choices-font-size-sm: 12px !default;
+$choices-guttering: 24px !default;
+$choices-border-radius: 2.5px !default;
+$choices-border-radius-item: 20px !default;
+$choices-bg-color: #f9f9f9 !default;
+$choices-bg-color-disabled: #eaeaea !default;
+$choices-bg-color-dropdown: #fff !default;
+$choices-text-color: #333 !default;
+$choices-keyline-color: #ddd !default;
+$choices-primary-color: #00bcd4 !default;
+$choices-disabled-color: #eaeaea !default;
+$choices-highlight-color: $choices-primary-color !default;
+$choices-button-dimension: 8px !default;
+$choices-button-offset: 8px !default;
+$choices-icon-cross: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==") !default;
+$choices-icon-cross-inverse: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==") !default;
+$choices-z-index: 1 !default;
+
+.#{$choices-selector} {
+  position: relative;
+  overflow: hidden;
+  margin-bottom: $choices-guttering;
+  font-size: $choices-font-size-lg;
+
+  &:focus {
+    outline: none;
+  }
+
+  &:last-child {
+    margin-bottom: 0;
+  }
+
+  &.is-open {
+    overflow: visible;
+  }
+
+  &.is-disabled {
+    .#{$choices-selector}__inner,
+    .#{$choices-selector}__input {
+      background-color: $choices-bg-color-disabled;
+      cursor: not-allowed;
+      user-select: none;
+    }
+    .#{$choices-selector}__item {
+      cursor: not-allowed;
+    }
+  }
+
+  [hidden] {
+    display: none !important;
+  }
+}
+
+.#{$choices-selector}[data-type*='select-one'] {
+  cursor: pointer;
+  .#{$choices-selector}__inner {
+    padding-bottom: 7.5px;
+  }
+  .#{$choices-selector}__input {
+    display: block;
+    width: 100%;
+    padding: 10px;
+    border-bottom: 1px solid $choices-keyline-color;
+    background-color: #fff;
+    margin: 0;
+  }
+  .#{$choices-selector}__button {
+    background-image: $choices-icon-cross-inverse;
+    padding: 0;
+    background-size: 8px;
+    position: absolute;
+    top: 50%;
+    right: 0;
+    margin-top: -10px;
+    margin-right: 25px;
+    height: 20px;
+    width: 20px;
+    border-radius: 10em;
+    opacity: 0.25;
+
+    &:hover,
+    &:focus {
+      opacity: 1;
+    }
+
+    &:focus {
+      box-shadow: 0 0 0 2px $choices-highlight-color;
+    }
+  }
+  .#{$choices-selector}__item[data-value=''] .#{$choices-selector}__button {
+    display: none;
+  }
+
+  &::after {
+    content: "";
+    height: 0;
+    width: 0;
+    border-style: solid;
+    border-color: $choices-text-color transparent transparent transparent;
+    border-width: 5px;
+    position: absolute;
+    right: 11.5px;
+    top: 50%;
+    margin-top: -2.5px;
+    pointer-events: none;
+  }
+
+  &.is-open::after {
+    border-color: transparent transparent $choices-text-color transparent;
+    margin-top: -7.5px;
+  }
+
+  &[dir="rtl"] {
+    &::after {
+      left: 11.5px;
+      right: auto;
+    }
+    .#{$choices-selector}__button {
+      right: auto;
+      left: 0;
+      margin-left: 25px;
+      margin-right: 0;
+    }
+  }
+}
+
+.#{$choices-selector}[data-type*='select-multiple'],
+.#{$choices-selector}[data-type*='text'] {
+  .#{$choices-selector}__inner {
+    cursor: text;
+  }
+  .#{$choices-selector}__button {
+    position: relative;
+    display: inline-block;
+    margin-top: 0;
+    margin-right: -$choices-button-offset * 0.5;
+    margin-bottom: 0;
+    margin-left: $choices-button-offset;
+    padding-left: $choices-button-offset * 2;
+    border-left: 1px solid color.adjust($choices-primary-color, $lightness: -10%);
+    background-image: $choices-icon-cross;
+    background-size: $choices-button-dimension;
+    width: $choices-button-dimension;
+    line-height: 1;
+    opacity: 0.75;
+    border-radius: 0;
+
+    &:hover,
+    &:focus {
+      opacity: 1;
+    }
+  }
+}
+
+.#{$choices-selector}__inner {
+  display: inline-block;
+  vertical-align: top;
+  width: 100%;
+  background-color: $choices-bg-color;
+  padding: 7.5px 7.5px 3.75px;
+  border: 1px solid $choices-keyline-color;
+  border-radius: $choices-border-radius;
+  font-size: $choices-font-size-md;
+  min-height: 44px;
+  overflow: hidden;
+
+  .is-focused &,
+  .is-open & {
+    border-color: color.adjust($choices-keyline-color, $lightness: -15%);
+  }
+
+  .is-open & {
+    border-radius: $choices-border-radius $choices-border-radius 0 0;
+  }
+
+  .is-flipped.is-open & {
+    border-radius: 0 0 $choices-border-radius $choices-border-radius;
+  }
+}
+
+.#{$choices-selector}__list {
+  margin: 0;
+  padding-left: 0;
+  list-style: none;
+
+  &[aria-expanded] {
+    @extend %choices-dropdown;
+  }
+}
+
+.#{$choices-selector}__list--single {
+  display: inline-block;
+  padding: 4px 16px 4px 4px;
+  width: 100%;
+
+  [dir="rtl"] & {
+    padding-right: 4px;
+    padding-left: 16px;
+  }
+  .#{$choices-selector}__item {
+    width: 100%;
+  }
+}
+
+.#{$choices-selector}__list--multiple {
+  display: inline;
+  .#{$choices-selector}__item {
+    display: inline-block;
+    vertical-align: middle;
+    border-radius: $choices-border-radius-item;
+    padding: 4px 10px;
+    font-size: $choices-font-size-sm;
+    font-weight: 500;
+    margin-right: 3.75px;
+    margin-bottom: 3.75px;
+    background-color: $choices-primary-color;
+    border: 1px solid color.adjust($choices-primary-color, $lightness: -5%);
+    color: #fff;
+    word-break: break-all;
+    box-sizing: border-box;
+
+    &[data-deletable] {
+      padding-right: 5px;
+    }
+
+    [dir="rtl"] & {
+      margin-right: 0;
+      margin-left: 3.75px;
+    }
+
+    &.is-highlighted {
+      background-color: color.adjust($choices-primary-color, $lightness: -5%);
+      border: 1px solid color.adjust($choices-primary-color, $lightness: -10%);
+    }
+
+    .is-disabled & {
+      background-color: color.adjust($choices-disabled-color, $lightness: -25%);
+      border: 1px solid color.adjust($choices-disabled-color, $lightness: -35%);
+    }
+  }
+}
+
+%choices-dropdown {
+  visibility: hidden;
+  z-index: $choices-z-index;
+  position: absolute;
+  width: 100%;
+  background-color: $choices-bg-color-dropdown;
+  border: 1px solid $choices-keyline-color;
+  top: 100%;
+  margin-top: -1px;
+  border-bottom-left-radius: $choices-border-radius;
+  border-bottom-right-radius: $choices-border-radius;
+  overflow: hidden;
+  word-break: break-all;
+  will-change: visibility;
+
+  &.is-active {
+    visibility: visible;
+  }
+
+  .is-open & {
+    border-color: color.adjust($choices-keyline-color, $lightness: -15%);
+  }
+
+  .is-flipped & {
+    top: auto;
+    bottom: 100%;
+    margin-top: 0;
+    margin-bottom: -1px;
+    border-radius: 0.25rem 0.25rem 0 0;
+  }
+  .#{$choices-selector}__list {
+    position: relative;
+    max-height: 300px;
+    overflow: auto;
+    -webkit-overflow-scrolling: touch;
+    will-change: scroll-position;
+  }
+  .#{$choices-selector}__item {
+    position: relative;
+    padding: 10px;
+    font-size: $choices-font-size-md;
+
+    [dir="rtl"] & {
+      text-align: right;
+    }
+  }
+  .#{$choices-selector}__item--selectable {
+    @media (min-width: 640px) {
+      padding-right: 100px;
+
+      &::after {
+        content: attr(data-select-text);
+        font-size: $choices-font-size-sm;
+        opacity: 0;
+        position: absolute;
+        right: 10px;
+        top: 50%;
+        transform: translateY(-50%);
+      }
+
+      [dir="rtl"] & {
+        text-align: right;
+        padding-left: 100px;
+        padding-right: 10px;
+
+        &::after {
+          right: auto;
+          left: 10px;
+        }
+      }
+    }
+
+    &.is-highlighted {
+      background-color: color.mix(#000, #fff, 5%);
+
+      &::after {
+        opacity: 0.5;
+      }
+    }
+  }
+}
+
+.#{$choices-selector}__list--dropdown {
+  @extend %choices-dropdown;
+}
+
+.#{$choices-selector}__item {
+  cursor: default;
+}
+
+.#{$choices-selector}__item--selectable {
+  cursor: pointer;
+}
+
+.#{$choices-selector}__item--disabled {
+  cursor: not-allowed;
+  user-select: none;
+  opacity: 0.5;
+}
+
+.#{$choices-selector}__heading {
+  font-weight: 600;
+  font-size: $choices-font-size-sm;
+  padding: 10px;
+  border-bottom: 1px solid color.adjust($choices-keyline-color, $lightness: 10%);
+  color: color.adjust(#333, $lightness: 30%);
+}
+
+.#{$choices-selector}__button {
+  text-indent: -9999px;
+  appearance: none;
+  border: 0;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  background-position: center;
+  cursor: pointer;
+
+  &:focus {
+    outline: none;
+  }
+}
+
+.#{$choices-selector}__input {
+  display: inline-block;
+  vertical-align: baseline;
+  background-color: $choices-bg-color;
+  font-size: $choices-font-size-md;
+  margin-bottom: 5px;
+  border: 0;
+  border-radius: 0;
+  max-width: 100%;
+  padding: 4px 0 4px 2px;
+
+  &:focus {
+    outline: 0;
+  }
+
+  &::-webkit-search-decoration,
+  &::-webkit-search-cancel-button,
+  &::-webkit-search-results-button,
+  &::-webkit-search-results-decoration {
+    display: none;
+  }
+
+  &::-ms-clear,
+  &::-ms-reveal {
+    display: none;
+    width: 0;
+    height: 0;
+  }
+
+  [dir="rtl"] & {
+    padding-right: 2px;
+    padding-left: 0;
+  }
+}
+
+.#{$choices-selector}__placeholder {
+  opacity: 0.5;
+}
+
+/* =====  End of Choices  ====== */
diff --git a/my_flask_app/static/script.js b/my_flask_app/static/script.js
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..556fcd016810c6c555a2d63047420db129fc5476 100644
--- a/my_flask_app/static/script.js
+++ b/my_flask_app/static/script.js
@@ -0,0 +1,298 @@
+function toggleSidebar() {
+    var sidebar = document.getElementById("sidebar1");
+    var content = document.getElementById("content");
+    if (sidebar.classList.contains("collapsed-sidebar")) {
+        sidebar.classList.remove("collapsed-sidebar");
+        content.style.marginLeft = "15%";
+    } else {
+        sidebar.classList.add("collapsed-sidebar");
+        content.style.marginLeft = "0px";
+    }
+}
+
+function toggleTerminal() {
+    var terminal = document.getElementById('terminal');
+    terminal.classList.toggle('active');
+}
+
+function closeTerminal() {  
+    var terminal = document.getElementById('terminal');
+    terminal.style.height = `0 px`;
+}
+
+function makeSidebarResizable(sidebarId, handleId, isLeftHandle) {
+    var isResizing = false;
+    var lastDownX = 0;
+    var sidebar = document.getElementById(sidebarId);
+    var handle = document.getElementById(handleId);
+
+    if (!sidebar || !handle) {
+        console.error('Sidebar or handle not found!');
+        return; // Exit the function if elements are not found
+    }
+
+    handle.addEventListener('mousedown', function(e) {
+        isResizing = true;
+        lastDownX = e.clientX;
+        e.preventDefault();
+    }, false);
+
+    // console.log("isLeftHandle: ", isLeftHandle);
+
+    window.addEventListener('mousemove', function(e) {
+        if (!isResizing) { return; }
+
+        var currentX = e.clientX;
+        var offsetX = e.clientX - lastDownX;
+
+        if (isLeftHandle) {
+            sidebar.style.width = window.innerWidth - e.clientX + 'px';
+        } else {
+            var newWidth = parseInt(getComputedStyle(sidebar).width, 10) + offsetX;
+            sidebar.style.width = newWidth + 'px';
+        }
+
+        lastDownX = e.clientX;
+    }, false);
+
+    window.addEventListener('mouseup', function(e) {
+        isResizing = false;
+    });
+}
+
+function showErdCanvas(erdCanvas, zoomInBtn, zoomOutBtn, erdImage) {
+    var canvas = document.getElementById(erdCanvas);
+    var ctx = canvas.getContext("2d");
+
+    var imageData =  erdImage; //var imageData = '{{ image }}';
+
+    var image = new Image();
+    image.onload = function () {
+        // Set canvas size based on the image dimensions
+        canvas.width = image.width;
+        canvas.height = image.height;
+
+        // Draw the image on the canvas
+        ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
+    };
+    image.src = "data:image/png;base64," + imageData;
+
+    // Set transform origin to top left corner
+    canvas.style.transformOrigin = '0 0';
+
+    // Add zoom in/out functionality
+    var scale = 1.0;
+    var zoomInBtn = document.getElementById(zoomInBtn);
+    var zoomOutBtn = document.getElementById(zoomOutBtn);
+
+    zoomInBtn.addEventListener("click", function () {
+        // Zoom in
+        scale += 0.1;
+        scale = Math.min(scale, 3.0);
+        canvas.style.transform = "scale(" + scale + ")";
+    });
+
+    zoomOutBtn.addEventListener("click", function () {
+        // Zoom out
+        scale -= 0.1;
+        scale = Math.max(scale, 0.1);
+        canvas.style.transform = "scale(" + scale + ")";
+    });
+}
+
+// Function to handle the click on a dropped item
+function handleDroppedItemClick(itemName) {
+    // Send a POST request to Flask backend with the item name
+    fetch('/get-table-data', {
+        method: 'POST',
+        body: JSON.stringify({ 'table_name': itemName }),
+        headers: {
+            'Content-Type': 'application/json'
+        }
+    })
+    .then(response => response.json())
+    .then(data => {
+        // Display the table content in the terminal body
+        document.querySelector('.terminal-body').innerHTML = data.html_table;
+
+        // Assign data.table_columns to the select element
+        const selectElement = document.getElementById('select_column');
+        selectElement.innerHTML = '';
+        data.table_columns.forEach(column => {
+            const optionElement = document.createElement('option');
+            optionElement.value = column;
+            optionElement.textContent = column;
+            selectElement.appendChild(optionElement);
+        });
+
+        // toggleTerminal(); // Show the terminal
+    })
+    .catch(error => console.error('Error:', error));
+}
+
+function handleLabelColumnClick(label) {
+    // Send a POST request to Flask backend with the item name
+    fetch('/get-label-column', {
+        method: 'POST',
+        body: JSON.stringify({ 'label_column_name': label }),
+        headers: {
+            'Content-Type': 'application/json'
+        }
+    })
+    .then(response => response.json())
+    .then(data => {
+        // Assign data.table_columns to the select element
+        const selectElement = document.getElementById('select_label_values');
+        selectElement.innerHTML = '';
+        data.table_columns.forEach(label_value => {
+            const optionElement = document.createElement('option');
+            optionElement.value = label_value;
+            optionElement.textContent = label_value;
+            selectElement.appendChild(optionElement);
+        });
+
+        // toggleTerminal(); // Show the terminal
+    })
+    .catch(error => console.error('Error:', error));
+}
+
+function makeTerminalResizable() {
+    const terminal = document.getElementById('terminal');
+    const terminalHeader = document.getElementById('terminal-header');
+    let isResizing = false;
+
+    terminalHeader.addEventListener('mousedown', function(e) {
+        // toggleTerminal(); 
+        isResizing = true;
+    });
+
+    window.addEventListener('mousemove', function(e) {
+        if (isResizing) {
+            // Calculate the new height of the terminal
+            const newHeight = window.innerHeight - e.clientY;
+            terminal.style.height = `${newHeight}px`;
+        }
+    });
+
+    window.addEventListener('mouseup', function(e) {
+        isResizing = false;
+    });
+}
+
+function showOptions(clickedItem) {
+    // Get the options container
+    var optionsContainer = document.getElementById('options-container');
+    
+    // Position the options container near the clicked item
+    optionsContainer.style.display = 'block';
+    optionsContainer.style.left = clickedItem.getBoundingClientRect().left + 'px';
+    optionsContainer.style.top = clickedItem.getBoundingClientRect().bottom + 'px';
+    
+    // Populate options specific to the clicked item
+    populateOptions(clickedItem.textContent.trim());
+}
+
+function populateOptions(tableName) {
+    // Logic to populate options based on the table name
+    // This will depend on the specific options you want to show
+    return 'abc';
+}
+
+
+
+document.addEventListener('DOMContentLoaded', function() {
+    // Show the ERD canvas
+    showErdCanvas("erdCanvas1", "zoomInBtn1", "zoomOutBtn1", '{{image1}}');
+    showErdCanvas("erdCanvas2", "zoomInBtn2", "zoomOutBtn2", '{{image2}}');
+    showErdCanvas("erdCanvas3", "zoomInBtn3", "zoomOutBtn3", '{{image2}}');
+
+    // Resize the sidebars
+    makeSidebarResizable('sidebar1', 'resize-handle-right', false);
+    makeSidebarResizable('sidebar2', 'resize-handle-left', true);
+    makeTerminalResizable();
+    toggleTerminal();
+
+    // Listen for the 'added' event on the target sortable list
+    UIkit.util.on('#dropped_items', 'added removed', function (event) {
+        var item = event.detail[1]; // Adjusted to access the second item
+        if (item) { // Ensure item is not undefined
+            var itemName = item.textContent.trim();
+            var action = event.type;
+            console.log(action, itemName);
+
+            // Send the item name to the Flask backend
+            fetch('/handle-drop', {
+                method: 'POST',
+                body: JSON.stringify({'item': itemName, 'action': action}),
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                showErdCanvas("erdCanvas2", "zoomInBtn2", "zoomOutBtn2", data.image2);
+                showErdCanvas("erdCanvas3", "zoomInBtn3", "zoomOutBtn3", data.image2);
+            })
+            .catch(error => console.error('Error:', error));
+        }
+    });
+
+    // document.getElementById('resetButton').addEventListener('click', function(event) {
+    //     event.preventDefault();  // Prevent the default form submission
+
+    //     // Send a POST request to the server
+    //     fetch('/handle-drop', {
+    //         method: 'POST',
+    //         body: JSON.stringify({'reset': true}),
+    //         headers: {
+    //             'Content-Type': 'application/json'
+    //         }
+    //     })
+    //     .then(response => response.json())
+    //     .then(data => {
+    //         // let tables_list = data['tables'];
+    //         document.getElementById('tables').innerHTML = data.tables;
+    //         // for (let i = 0; i < tables_list.length; i++) {
+    //         //     let table = tables_list[i];
+    //         //     let item_inner = document.createElement('div');
+    //         //     item_inner.className = 'ist-group-item-action uk-card uk-card-default uk-card-body uk-card-small';
+    //         //     item_inner.style = "height: 15px; padding-top: 5px;"
+    //         //     item_inner.textContent = table;
+
+    //         //     let item_outer = document.createElement('div');
+    //         //     item_outer.className = 'uk-margin';
+    //         //     item_outer.style = "height: 15px; margin-bottom: -4px;"
+    //         //     item_outer.appendChild(item_inner);
+
+    //         //     document.getElementById('show_tables1').appendChild(item_outer);
+    //         // }
+    //         document.getElementById('dropped_items').innerHTML =data.dropped_items;
+    //         showErdCanvas("erdCanvas2", "zoomInBtn2", "zoomOutBtn2", data.image2);
+    //         showErdCanvas("erdCanvas3", "zoomInBtn3", "zoomOutBtn3", data.image2);
+
+    //     })
+    //     .catch(error => console.error('Error:', error));
+    // });
+
+    // Attach click event listener to each dropped item
+    document.getElementById('dropped_items').addEventListener('click', function(event) {
+        let target = event.target;
+        while (target != this) {
+            if (target.matches('.uk-card')) {
+                handleDroppedItemClick(target.textContent.trim());
+                return;
+            }
+            target = target.parentNode;
+        }
+
+        showOptions(target.textContent.trim());
+    });
+
+    document.getElementById('close-terminal').addEventListener('click', function() {
+        closeTerminal();
+    });
+
+    
+
+});
+
diff --git a/my_flask_app/templates/app.html b/my_flask_app/templates/app.html
index 7895a8d0add68c77eb4f2336094d734c1d974d24..4c70901437df8dbfea6e5373c16f58cd6649dba7 100644
--- a/my_flask_app/templates/app.html
+++ b/my_flask_app/templates/app.html
@@ -4,12 +4,24 @@
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <title>Database ERD Viewer</title>
+    <script
+        src="https://code.jquery.com/jquery-3.4.1.min.js"
+        integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
+        crossorigin="anonymous">
+    </script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.jquery.min.js"></script>
+    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.min.css" />
+    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
+
     <link rel="stylesheet" href="{{ url_for('static', filename='uikit-3.18.0/css/uikit.min.css') }}" />
     <script src="{{ url_for('static', filename='uikit-3.18.0/js/uikit.min.js') }}"></script>
     <script src="{{ url_for('static', filename='uikit-3.18.0/js/uikit-icons.min.js') }}"></script>
+    
     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
     <script src="https://d3js.org/d3.v5.min.js"></script>
     <script src="https://unpkg.com/d3-graphviz@2.6.1/build/d3-graphviz.min.js"></script>
+    <!-- <link rel="stylesheet" href="{{ url_for('static', filename='choices.js/public/assets/styles/choices.min.css')}}">
+    <script src="{{ url_for('static', filename='choices.js/public/assets/scripts/choices.min.js')}}"></script> -->
     <style>
         body { 
             /* position: fixed; */
@@ -166,17 +178,17 @@
             transition: height 0.1s;
         }        
         .terminal-header {
-            height: 3vh;
+            height: 4vh;
             background-color: #5ea9e2;
             cursor: ns-resize; /* This cursor indicates vertical resizing */
             /* display: realtive; */
             justify-content: space-between;
             align-items: center;
-            margin-top: -3vh;
+            margin-top: -4vh;
         }
         .terminal-body {
             padding: 1rem;
-            display: none;
+            display: none;      
         }
         .terminal.active {
             transform: translateY(0); /* Slide into view */
@@ -204,8 +216,6 @@
         .terminal.resizing .terminal-header {
             cursor: ns-resize;
         }
-
-
         .options-container {
             /* ... your styles ... */
             position: absolute;
@@ -215,54 +225,42 @@
             padding: 10px;
             z-index: 100; /* Make sure it appears on top */
         }
+        select {
+            width: 100%;
+        }
+        .chosen-container {
+            width: 100% !important; /* Use !important to override inline styles if necessary */
+        }
+        /* Removes the Chosen removal button image */
+        .chosen-container-multi .chosen-choices li.search-choice .search-choice-close {
+            background: none !important;
+        }
+        .chosen-container-multi .chosen-choices li.search-choice .search-choice-close:after {
+            font-family: "Font Awesome 5 Brands", sans-serif; 
+            content: "\f099";
+            color: red;
+            font-size: 12px;
+        }
     </style>
 </head>
 <body>
+    <!-- <script src="{{ url_for('static', filename='script.js')}}"></script> -->
     <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script>
     
     <div id="sidebar1">   <!--class="uk-panel uk-panel-scrollable" -->
-        <h4>Database:</h4>
-        <p style="margin-top: -10px; padding-left: 5px;">{{database}}.db</p>
-
-
-        <!-- <ul uk-accordion>
-            <li class="uk-open">
-                <a class="uk-accordion-title" href="#" style="margin-top: -8px; margin-bottom: -1px;">Schema:</a>
-                <div class="uk-accordion-content" style="margin-top: -3px; border: 1px solid black;">
-                    <form method="post">
-                        <input class="form-check-input" id="flexCheckDefault" style="margin: 2px 2px 2px 5px; border-color: black;"  type="checkbox" name="show_all" value="True" onchange="this.form.submit()" {{ 'checked' if show_all else '' }}>
-                        <label class="form-check-label" for="flexCheckDefault" style="margin-top: 3px;">
-                            Primary Tables
-                        </label>
-                        <select class="form-select" name="schema" onchange='this.form.submit()' style="padding: 0px 1px 2px 1px;">
-                            <option value="">Choose here</option>
-                            {% for schema in schemas %}
-                            <option value="{{ schema }}" {% if schema == schema_Selected %} selected {% endif %}>{{ schema }}</option>
-                            {% endfor %}
-                        </select>
-                    </form>
-                </div>
-            </li>
-            <li>
-                <a class="uk-accordion-title" href="#">Time:</a>
-            </li>
-            <li>
-                <a class="uk-accordion-title" href="#">Object:</a>
-            </li>
-            <li>
-                <a class="uk-accordion-title" href="#">Label:</a>
-            </li>
-        </ul> -->
-
-        <div class="accordion" id="accordionExample">
+        <legend>Database:</legend>
+        <div class="mb-3">
+            <label for="disabledTextInput" class="form-label" style="margin-top: 10px;">{{database}}.db</label>
+        </div>
+        <div class="accordion" style="margin-top: 5px;">
             <div class="accordion-item">
                 <h2 class="accordion-header">
-                  <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
+                  <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
                     Schema
                   </button>
                 </h2>
-                <div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionExample">
+                <div id="collapseOne" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
                   <div class="accordion-body">
                     <form method="post">
                         <!-- <label> -->
@@ -284,11 +282,11 @@
               </div>
             <div class="accordion-item">
               <h2 class="accordion-header">
-                <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="true" aria-controls="collapseTwo">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="true" aria-controls="collapseTwo">
                   Time
                 </button>
               </h2>
-              <div id="collapseTwo" class="accordion-collapse collapse show" data-bs-parent="#accordionExample">
+              <div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
                 <div class="accordion-body">
                   <!-- <strong>This is the first item's accordion body.</strong> It is shown by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow. -->
                 </div>
@@ -321,12 +319,12 @@
           </div>
 
         <h4 style="margin-top: 0px;">Tables:</h4>
-        <div id="table_list" style="margin-bottom: 5px; margin-top: -10px;">
+        <div class="border border-secondary rounded" id="table_list" style="margin-bottom: 5px; margin-top: -10px;">
             <div id="table_list_source">
-                <div uk-sortable="group: sortable-group" class="uk-list uk-list-collapse">
+                <div id="show_tables1" uk-sortable="group: sortable-group" class="uk-list  uk-list-collapse">
                     {% for table in tables %}
-                        <div class="uk-margin" style="height: 15px; margin-bottom: -4px;">
-                            <div class="uk-card uk-card-default uk-card-body uk-card-small" style="height: 15px; padding-top: 5px;">{{ table }}</div>
+                        <div id="show_tables2" class="uk-margin" style="height: 15px; margin-bottom: -4px;">
+                            <div class="list-group-item-action uk-card uk-card-default uk-card-body uk-card-small " style="height: 15px; padding-top: 5px;">{{ table }}</div>
                         </div>
                     {% endfor %}
                 </div>
@@ -334,109 +332,173 @@
         </div>
 
         <h4 style="margin-top: -4px;">Target Tables:</h4>
-        <div id="table_list" style="margin-bottom: 5px; margin-top: -20px;">
+        <div class="border border-secondary rounded" id="table_list" >
             <div id="dropped_items">
                 <div uk-sortable="group: sortable-group" class="uk-list uk-list-collapse ">
                     {% for item in dropped_items %}
                         <div class="uk-margin" style="height: 15px; margin-bottom: -4px;">
-                            <div class="uk-card uk-card-default uk-card-body uk-card-small" onclick="showOptions(this)" style="height: 15px; padding-top: 5px;">{{ item }}</div>
+                            <div class="list-group-item-action uk-card uk-card-default uk-card-body uk-card-small" onclick="showOptions(this)" style="height: 15px; padding-top: 5px;">{{ item }}</div>
                         </div>
                     {% endfor %}
                 </div>
             </div>
         </div>
-        <!-- <div id="resize-handle-right" class="resize-handle-right"></div> -->
+        <!-- don't use form, try use javascript to handle the reset button -->
+        <form action="/handle-drop" method="POST">
+            <button id="resetButton" type="submit" class="btn btn-secondary btn-sm">Reset</button>
+        </form>
     </div> 
     
     <div id="content">
-        <!-- <div class="uk-margin-small-top" style="z-index: 0;"> -->
-            <button class="uk-button uk-button-default uk-button-small custom-push-button" type="button" uk-toggle="target: #sidebar1" onclick="toggleSidebar()"></button>      
-            <ul uk-tab class="uk-flex-center" data-uk-tab="{connect:'#my-id'}" style="margin-top: 10px; z-index: 0;">
-                 <li><a href="#">ERD</a></li>
-                <li><a href="#">Other Content</a></li>
-            </ul>
-        <!-- </div> -->
+        <button class="uk-button uk-button-default uk-button-small custom-push-button" type="button" uk-toggle="target: #sidebar1" onclick="toggleSidebar()"></button>      
+        <ul uk-tab class="uk-flex-center" data-uk-tab="{connect:'#my-id'}" style="margin-top: 10px; z-index: 0;">
+                <li><a href="#">ERD</a></li>
+            <li><a href="#">Other Content</a></li>
+        </ul>
 
         <!-- <div> -->
-            <ul id="my-id" class="uk-switcher">
-                <li><a href="#" id="autoplayer" data-uk-switcher-item="next"></a>
-                    <div style="display: flex; flex-direction: row; height: 85vh;">
-
-                        <div style="flex: 0,3; display: flex; flex-direction: column;">
-                            <h4>Filtered ERD</h4>
-                            <div class="uk-panel uk-panel-scrollable" id="canvasContainer" style="margin-right: -1px; min-width: 30%;">
-                                <canvas id="erdCanvas1" style="min-width: 30%;"></canvas>
-                                <div id="zoomButtons">
-                                    <button class="uk-button uk-button-default uk-button-small custom-zoom-button" id="zoomInBtn1" uk-icon="plus-circle"></button>
-                                    <button class="uk-button uk-button-default uk-button-small custom-zoom-button" id="zoomOutBtn1" uk-icon="minus-circle"></button>
-                                </div>
+        <ul id="my-id" class="uk-switcher">
+            <li><a href="#" id="autoplayer" data-uk-switcher-item="next"></a>
+                <div style="display: flex; flex-direction: row; height: 85vh;">
+
+                    <div style="flex: 0,3; display: flex; flex-direction: column;">
+                        <h4>Filtered ERD</h4>
+                        <div class="uk-panel uk-panel-scrollable" id="canvasContainer" style="margin-right: -1px; min-width: 30%;">
+                            <canvas id="erdCanvas1" style="min-width: 30%;"></canvas>
+                            <div id="zoomButtons">
+                                <button class="uk-button uk-button-default uk-button-small custom-zoom-button" id="zoomInBtn1" uk-icon="plus-circle"></button>
+                                <button class="uk-button uk-button-default uk-button-small custom-zoom-button" id="zoomOutBtn1" uk-icon="minus-circle"></button>
                             </div>
                         </div>
+                    </div>
 
-                        <div style="flex: 0,7; display: flex; flex-direction: column;">
-                            <h4>Target ERD</h4>
-                            <div class="uk-panel uk-panel-scrollable" id="canvasContainer" style="min-width: 20%; max-width: 70%;">
-                                <canvas id="erdCanvas2" style="min-width: 30%;"></canvas>
-                                <div id="zoomButtons">
-                                    <button class="uk-button uk-button-default uk-button-small custom-zoom-button"id="zoomInBtn2" uk-icon="plus-circle"></button>
-                                    <button class="uk-button uk-button-default uk-button-small custom-zoom-button"id="zoomOutBtn2" uk-icon="minus-circle"></button>
-                                </div>
+                    <div style="flex: 0,7; display: flex; flex-direction: column;">
+                        <h4>Target ERD</h4>
+                        <div class="uk-panel uk-panel-scrollable" id="canvasContainer" style="min-width: 20%; max-width: 70%;">
+                            <canvas id="erdCanvas2" style="min-width: 30%;"></canvas>
+                            <div id="zoomButtons">
+                                <button class="uk-button uk-button-default uk-button-small custom-zoom-button"id="zoomInBtn2" uk-icon="plus-circle"></button>
+                                <button class="uk-button uk-button-default uk-button-small custom-zoom-button"id="zoomOutBtn2" uk-icon="minus-circle"></button>
                             </div>
                         </div>
-
                     </div>
-                </li>
-
-                <li>
-                    <div style="display: flex; flex-direction: row; height: 87vh;">
-
-                        <div style="flex: 0,4; display: flex; flex-direction: column;">
-                            <h4>Target ERD</h4>
-                            <div class="uk-panel uk-panel-scrollable" id="canvasContainer" style="margin-right: -1px; min-width: 30%;">
-                                <canvas id="erdCanvas3" style="min-width: 30%;"></canvas>
-                                <div id="zoomButtons">
-                                    <button class="uk-button uk-button-default uk-button-small custom-zoom-button" id="zoomInBtn3" uk-icon="plus-circle"></button>
-                                    <button class="uk-button uk-button-default uk-button-small custom-zoom-button" id="zoomOutBtn3" uk-icon="minus-circle"></button>
-                                </div>
+
+                </div>
+            </li>
+
+            <li>
+                <div style="display: flex; flex-direction: row; height: 87vh;">
+
+                    <div style="flex: 0,4; display: flex; flex-direction: column;">
+                        <h4>Target ERD</h4>
+                        <div class="uk-panel uk-panel-scrollable" id="canvasContainer" style="margin-right: -1px; min-width: 30%;">
+                            <canvas id="erdCanvas3" style="min-width: 30%;"></canvas>
+                            <div id="zoomButtons">
+                                <button class="uk-button uk-button-default uk-button-small custom-zoom-button" id="zoomInBtn3" uk-icon="plus-circle"></button>
+                                <button class="uk-button uk-button-default uk-button-small custom-zoom-button" id="zoomOutBtn3" uk-icon="minus-circle"></button>
                             </div>
                         </div>
+                    </div>
 
-                        <div style="flex: 0,6; display: flex; width: 60%; max-width: 70%; flex-direction: column; border: 1px solid blue;">
-                            <div id="options-container" class="options-container" style="display: block;">
-                                <div class="uk-margin">
-                                    <div class="uk-form-label">Type</div><br>
-                                    <div class="uk-form-controls">
-                                        <label><input class="uk-radio" type="radio" name="radio1"> Measurement </label><br>
-                                        <label><input class="uk-radio" type="radio" name="radio1"> Event </label><br>
-                                        <label><input class="uk-radio" type="radio" name="radio1"> Segment </label><br>
-                                        <label><input class="uk-radio" type="radio" name="radio1"> Segment Data </label><br>
-                                        <label><input class="uk-radio" type="radio" name="radio1"> Object </label>
-                                    </div>
-                                </div>
+                    <div style="flex: 0,6; display: flex; width: 60%; max-width: 70%; flex-direction: column; border: 1px solid blue;">
+                    
+                    <fieldset>
+                        <legend>Data Header</legend>
+
+                        <div class="mb-3">
+                            <label class="form-label" style="margin-top: 14px">Type</label>
+                            <div class="uk-grid-small uk-child-width-auto uk-grid">
+                                <label><input class="uk-radio" type="radio" name="radio1"> Measurement </label><br><br>
+                                <label><input class="uk-radio" type="radio" name="radio1"> Event </label><br><br>
+                                <label><input class="uk-radio" type="radio" name="radio1"> Segment </label><br><br>
+                                <label><input class="uk-radio" type="radio" name="radio1"> Segment Data </label><br><br>
+                                <label><input class="uk-radio" type="radio" name="radio1"> Object </label>
                             </div>
                         </div>
 
+                        <div class="mb-3">
+                            <label class="form-label">Self-defined Label</label>
+                            <input type="text" id="defined-label" class="form-control">
+                            <button class="btn btn-primary" onclick="addLabel()">add</button>
+                        </div>
+                        
+                        <div class="mb-3">
+                            <label class="form-label">Label</label>
+                            
+                            <select id="select_column" class="form-select" name="column" onchange='handleLabelColumnClick(value)'>
+                            {% for column in columns %}
+                                <option>Column select...</option>
+                                <option value="{{label}}">{{ label }}</option>
+                            {% endfor %}
+                            </select> 
+                
+                            <select id=label_values class="form-select">
+                                <option>Label select...</option>
+
+                                <optgroup id="defined_label_values" label="self-defined label">
+                                {% for self_label in self_labels %}
+                                    <option value="{{self_label}}">{{ self_label }}</option>
+                                {% endfor %}
+                                </optgroup >
+
+                                <optgroup id="select_label_values" label="label column data">
+                                {% for label_value in label_values %}
+                                    <option value="{{label_value}}">{{ label_value }}</option>
+                                {% endfor %}
+                                </optgroup>
+                            </select>   
+                        </div>
+
+                        <div class="mb-3">
+                            <label class="form-label">Feature</label>
+                            <form method="POST" action = "/get_value_column" name = "chart_options"  style="width: 100%; border-color: aqua;">
+                                <select name = "value_column[]" data-placeholder="Value columns" multiple class="chosen-select" tabindex="8">
+                                    <option value="GB">GB</option>              
+                                    <option value="RU">RU</option>
+                                    {% for value_column in value_columns %}
+                                        <option value="{{ value_column }}">{{ value_column }}</option>
+                                    {% endfor %}
+                                </select>  
+                            
+                                <script> $(".chosen-select").chosen(); </script>
+                            
+                                <button type="submit" class="btn btn-primary">Submit</button>
+                            </form>     
+                        </div>
+                            <!-- <div class="mb-3">
+                                <div class="form-check">
+                                    <input class="form-check-input" type="checkbox" id="disabledFieldsetCheck">
+                                    <label class="form-check-label" for="disabledFieldsetCheck">
+                                    Can't check this
+                                    </label>
+                                </div>
+                            </div> -->
+                            <button type="submit" class="btn btn-primary">Submit</button>
+                    </fieldset>
                     </div>
-                </li>
 
-            </ul>
+                </div>
+            </li>
+
+        </ul>
         <!-- </div> -->
 
+
+
         <div class="custom-buttom-bar">
-                
             <div id="terminal" class="terminal">
                 <div id="terminal-header" class="terminal-header">
                     <span style="position: absolute; left: 5px;">Table</span> <span uk-icon="menu" style="position: absolute; right: 70vh;"></span>
-                    <span id="close-terminal" uk-icon="close" style="position: absolute; right: 1vh; z-index: 1000;"></span>
-                    <!-- <button class="uk-button"  style="max-width: 20px; height: 20px; z-index: 15;; position: absolute; margin-left: 90vh;" onclick="toggleTerminal()" uk-icon="close"></button> -->
+                    <!-- <span id="close-terminal" uk-icon="close" style="position: absolute; right: 1vh; z-index: 1000;"></span> -->
+                    <button type="button" class="btn-close"  aria-label="Close" data-bs-dismiss="modal" style="position: absolute; right: 1vh; margin-top: 1px; z-index: 1000;" onclick="toggleTerminal()"></button>
+                    <!-- <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> -->
                 </div>
-                <div id=terminal-body class="terminal-body uk-overflow-auto">
+                <div id=terminal-body class="terminal-body">
                     <p> No table selected.</p>
                 </div>
             </div>
-
         </div>
-        
+
     </div>
 
     <div id="sidebar2">
@@ -464,13 +526,42 @@
                 <button type="submit" class="btn btn-primary">Submit</button>
             </div>
         </form>
-        
-
 
         <div id="resize-handle-left" class="resize-handle-left"></div>
     </div> 
-
     <script>
+        function addLabel() {
+            const labelValue = document.getElementById('defined-label').value; // Get the value of the input
+
+            // Send the value to the server with fetch API
+            fetch('/add-self-defined-label', {
+                method: 'POST',
+                body: JSON.stringify({ 'self_defined_label': labelValue }),
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                const selectElement1 = document.getElementById('defined_label_values');
+                selectElement1.innerHTML = '';  // Clear existing options
+                data.defined_labels.forEach(label_value => {
+                    const optionElement = document.createElement('option');
+                    optionElement.value = label_value;
+                    optionElement.textContent = label_value;
+                    selectElement1.appendChild(optionElement);  // Append to selectElement1
+                });
+                // Handle response data from the server
+                console.log(data);
+            })
+            .catch(error => {
+                // Handle any error that occurred during the fetch
+                console.error('Error:', error);
+            });
+
+            labelValue.value = ''; // Clear the input field
+        }
+
         function toggleSidebar() {
             var sidebar = document.getElementById("sidebar1");
             var content = document.getElementById("content");
@@ -572,7 +663,7 @@
                 canvas.style.transform = "scale(" + scale + ")";
             });
         }
-        
+
         // Function to handle the click on a dropped item
         function handleDroppedItemClick(itemName) {
             // Send a POST request to Flask backend with the item name
@@ -587,11 +678,60 @@
             .then(data => {
                 // Display the table content in the terminal body
                 document.querySelector('.terminal-body').innerHTML = data.html_table;
+
+                // Assign data.table_columns to the select element
+                const selectElement = document.getElementById('select_column');
+                selectElement.innerHTML = '';
+                // Create default option
+                const optionElement = document.createElement('option');
+                optionElement.value = '';
+                optionElement.textContent = 'Label column select';
+                selectElement.appendChild(optionElement);
+                // Add the table columns as options
+                data.table_columns.forEach(column => {
+                    const optionElement = document.createElement('option');
+                    optionElement.value = column;
+                    optionElement.textContent = column;
+                    selectElement.appendChild(optionElement);
+                });
+
                 // toggleTerminal(); // Show the terminal
             })
             .catch(error => console.error('Error:', error));
         }
 
+        function handleLabelColumnClick(label) {
+            fetch('/get-label-column', {
+                method: 'POST',
+                body: JSON.stringify({ 'label_column_name': label }),
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                const selectElement1 = document.getElementById('defined_label_values');
+                selectElement1.innerHTML = '';  // Clear existing options
+                data.defined_labels.forEach(label_value => {
+                    const optionElement = document.createElement('option');
+                    optionElement.value = label_value;
+                    optionElement.textContent = label_value;
+                    selectElement1.appendChild(optionElement);  // Append to selectElement1
+                });
+
+                const selectElement2 = document.getElementById('select_label_values');
+                selectElement2.innerHTML = '';  // Clear existing options
+                data.table_labels.forEach(label_value => {
+                    const optionElement = document.createElement('option');
+                    optionElement.value = label_value;
+                    optionElement.textContent = label_value;
+                    selectElement2.appendChild(optionElement);  // Append to selectElement2
+                });
+            })
+            .catch(error => console.error('Error:', error));
+        }
+
+
         function makeTerminalResizable() {
             const terminal = document.getElementById('terminal');
             const terminalHeader = document.getElementById('terminal-header');
@@ -634,11 +774,14 @@
             return 'abc';
         }
 
+
+
         document.addEventListener('DOMContentLoaded', function() {
+            console.log('DOM is ready');
             // Show the ERD canvas
-            showErdCanvas("erdCanvas1", "zoomInBtn1", "zoomOutBtn1", '{{image1}}');
-            showErdCanvas("erdCanvas2", "zoomInBtn2", "zoomOutBtn2", '{{image2}}');
-            showErdCanvas("erdCanvas3", "zoomInBtn3", "zoomOutBtn3", '{{image2}}');
+            showErdCanvas("erdCanvas1", "zoomInBtn1", "zoomOutBtn1", '{{ image1 }}');
+            showErdCanvas("erdCanvas2", "zoomInBtn2", "zoomOutBtn2", '{{ image2 }}');
+            showErdCanvas("erdCanvas3", "zoomInBtn3", "zoomOutBtn3", '{{ image2 }}');
 
             // Resize the sidebars
             makeSidebarResizable('sidebar1', 'resize-handle-right', false);
@@ -657,7 +800,7 @@
                     // Send the item name to the Flask backend
                     fetch('/handle-drop', {
                         method: 'POST',
-                        body: JSON.stringify({'item': itemName, 'action': action}),
+                        body: JSON.stringify({ 'item': itemName, 'action': action }),
                         headers: {
                             'Content-Type': 'application/json'
                         }
@@ -670,7 +813,44 @@
                     .catch(error => console.error('Error:', error));
                 }
             });
-        
+
+            // document.getElementById('resetButton').addEventListener('click', function(event) {
+            //     event.preventDefault();  // Prevent the default form submission
+
+            //     // Send a POST request to the server
+            //     fetch('/handle-drop', {
+            //         method: 'POST',
+            //         body: JSON.stringify({'reset': true}),
+            //         headers: {
+            //             'Content-Type': 'application/json'
+            //         }
+            //     })
+            //     .then(response => response.json())
+            //     .then(data => {
+            //         // let tables_list = data['tables'];
+            //         document.getElementById('tables').innerHTML = data.tables;
+            //         // for (let i = 0; i < tables_list.length; i++) {
+            //         //     let table = tables_list[i];
+            //         //     let item_inner = document.createElement('div');
+            //         //     item_inner.className = 'ist-group-item-action uk-card uk-card-default uk-card-body uk-card-small';
+            //         //     item_inner.style = "height: 15px; padding-top: 5px;"
+            //         //     item_inner.textContent = table;
+
+            //         //     let item_outer = document.createElement('div');
+            //         //     item_outer.className = 'uk-margin';
+            //         //     item_outer.style = "height: 15px; margin-bottom: -4px;"
+            //         //     item_outer.appendChild(item_inner);
+
+            //         //     document.getElementById('show_tables1').appendChild(item_outer);
+            //         // }
+            //         document.getElementById('dropped_items').innerHTML =data.dropped_items;
+            //         showErdCanvas("erdCanvas2", "zoomInBtn2", "zoomOutBtn2", data.image2);
+            //         showErdCanvas("erdCanvas3", "zoomInBtn3", "zoomOutBtn3", data.image2);
+
+            //     })
+            //     .catch(error => console.error('Error:', error));
+            // });
+
             // Attach click event listener to each dropped item
             document.getElementById('dropped_items').addEventListener('click', function(event) {
                 let target = event.target;
@@ -689,10 +869,7 @@
                 closeTerminal();
             });
 
-
         });
-    
-        
     </script>
 </body>
 </html>