From c1ceafd2b6b36a8b19661d379aba1ed5da18eac3 Mon Sep 17 00:00:00 2001 From: "James A. Overton" Date: Mon, 29 Jul 2024 12:03:31 -0400 Subject: [PATCH] Update VALVE, improve list() handling and JavaScript --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/javascript/src/App.js | 53 ++++++++++++++++++++++++++++--------- src/javascript/src/index.js | 31 +++++++++++++++++----- src/resources/page.html | 49 ---------------------------------- src/serve.rs | 29 +++++++++++--------- 6 files changed, 84 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3801ba8..b2cd9b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1982,7 +1982,7 @@ dependencies = [ [[package]] name = "ontodev_valve" version = "0.2.2" -source = "git+https://github.com/ontodev/valve.rs?rev=1858972dec231f874c87f3fd930cbeef47a444d3#1858972dec231f874c87f3fd930cbeef47a444d3" +source = "git+https://github.com/ontodev/valve.rs?rev=6025da044de289861973a62f83484d9cca03c78f#6025da044de289861973a62f83484d9cca03c78f" dependencies = [ "anyhow", "async-recursion", diff --git a/Cargo.toml b/Cargo.toml index 0a4e621..d81b4e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rev = "f46fbd5450505644ed9970cef1ae14164699981f" [dependencies.ontodev_valve] # path = "../ontodev_demo/valve.rs" git = "https://github.com/ontodev/valve.rs" -rev = "1858972dec231f874c87f3fd930cbeef47a444d3" +rev = "6025da044de289861973a62f83484d9cca03c78f" [dependencies.ontodev_sqlrest] git = "https://github.com/ontodev/sqlrest.rs" diff --git a/src/javascript/src/App.js b/src/javascript/src/App.js index 11ae83e..a5b95fe 100644 --- a/src/javascript/src/App.js +++ b/src/javascript/src/App.js @@ -1,40 +1,67 @@ import { AsyncTypeahead } from 'react-bootstrap-typeahead'; -import { useState } from 'react'; +import { createRef, useState } from 'react'; import './App.css'; import 'bootstrap/dist/css/bootstrap.css'; import 'react-bootstrap-typeahead/css/Typeahead.css'; -function App() { - const table = document.getElementById("root").getAttribute("data-table"); - const column = document.getElementById("root").getAttribute("data-column"); +function App(args) { + // console.log("Starting App for", args, args.id, args.table, args.column); const [isLoading, setIsLoading] = useState(false); const [options, setOptions] = useState([]); + const handleSearch = (query: string) => { + // console.log("Starting search for", query); setIsLoading(true); - console.log("FETCH", `/table?text=${query}&column=type&format=json`); - fetch(`/${table}?text=${query}&column=${column}&format=json`) + const url = `../../${args.table}?text=${query}&column=${args.column}&format=json`; + // console.log("URL", url); + fetch(url) .then((resp) => resp.json()) .then((items) => { - // console.log("ITEMS", items); setOptions(items); setIsLoading(false); }); }; + // console.log("Starting App for", args, args.id, args.table, args.column); + const ref = createRef(); + var value = args.value; + var selected = [{"id": args.value, "label": args.value, "order": 1}]; + if (args.multiple) { + value = ""; + selected = args.value.trim().split(" ").filter((item) => { + return item.trim() !== ""; + }).map((item, order) => { + return {"id": item, "label": item, "order": order} + }); + } return (
{ - // console.log("SELECTED", selected); + // Set value of original input element to selected value. var values = selected.map((item) => item.id); - // console.log("VALUES", values); - var value = values.join(" "); - // console.log("VALUE", value); - document.getElementById("root-value").value = value; + document.getElementById(args.id).value = values.join(" "); + }} + onFocus={(event) => { + // Search for current values. + handleSearch(event.target.value); + }} + onBlur={(event) => { + // Set value of original input element to an invalid/incomplete value. + if (!args.multiple) { + document.getElementById(args.id).value = event.target.value; + } }} onSearch={handleSearch} + defaultInputValue={value} + defaultSelected={selected} options={options} - multiple="true" />
); diff --git a/src/javascript/src/index.js b/src/javascript/src/index.js index 593edf1..1fefe56 100644 --- a/src/javascript/src/index.js +++ b/src/javascript/src/index.js @@ -2,9 +2,28 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; -const root = ReactDOM.createRoot(document.getElementById('root')); -root.render( - - - -); +var typeaheads = document.getElementsByClassName('typeahead'); + +// console.log('TYPEAHEADS', typeaheads); +for (var i=0; i < typeaheads.length; i++) { + const typeahead = typeaheads[i]; + if (typeahead.tagName.toLowerCase() !== "input") { + continue; + } + // console.log('TYPEAHEAD', typeahead.id); + const div = document.createElement("div"); + typeahead.setAttribute("type", "hidden"); + typeahead.parentNode.insertBefore(div, typeahead); + const root = ReactDOM.createRoot(div); + root.render( + + + + ); +} diff --git a/src/resources/page.html b/src/resources/page.html index 946ccdc..5d61809 100644 --- a/src/resources/page.html +++ b/src/resources/page.html @@ -168,12 +168,6 @@ -
-
- - -
-
{% if page.elapsed %}

{{ page.elapsed }}ms

{% endif %} {% if page.repo %} @@ -224,49 +218,6 @@ }); } friendlifyMoments(); - - function configure_typeahead_form(node, table) { - if (!node.id || !node.id.endsWith("-typeahead-form")) { - return; - } - var col = node.id.split("-")[0]; - var bloodhound = new Bloodhound({ - datumTokenizer: Bloodhound.tokenizers.obj.nonword('label'), - queryTokenizer: Bloodhound.tokenizers.nonword, - sorter: function (a, b) { - return a.order - b.order; - }, - remote: { - url: `../../${table}?text=%QUERY&column=${col}&format=json`, - wildcard: '%QUERY', - transform: function (response) { - return bloodhound.sorter(response); - } - } - }); - $(node).typeahead({ - minLength: 0, - hint: false, - highlight: true - }, { - name: `${table}`, - source: bloodhound, - display: function (item) { - return item.label; - }, - limit: 40 - }); - $(node).bind('click', function (e) { - $(node).select(); - }); - var cls = node.className; - if (cls.includes("is-invalid")) { - var parent = node.parentElement; - var parentCls = parent.className; - parent.className = parentCls + " is-invalid"; - } - } - {% block body_end %}{% endblock %} diff --git a/src/serve.rs b/src/serve.rs index 972d235..0e1406c 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -1703,16 +1703,14 @@ fn get_row_as_form_map( let datatype = column_config.datatype; let structure = column_config.structure.split('(').collect::>()[0]; - let mut html_type; - let mut allowed_values = None; - if vec!["from", "in", "tree", "under"].contains(&structure) { - html_type = Some("search".into()); - } else { - (html_type, allowed_values) = get_html_type_and_values(config, &datatype, &None)?; - } - - if allowed_values != None && html_type == None { - html_type = Some("search".into()); + let (mut html_type, allowed_values) = get_html_type_and_values(config, &datatype, &None)?; + if html_type == None { + if allowed_values != None { + html_type = Some("search".into()); + } + if vec!["from", "in", "tree", "under"].contains(&structure) { + html_type = Some("search".into()); + } } let readonly; @@ -1725,6 +1723,7 @@ fn get_row_as_form_map( }; let hiccup_form_row = get_hiccup_form_row( + table_name, cell_header, &None, &allowed_values, @@ -1757,6 +1756,7 @@ fn get_row_as_form_map( } fn get_hiccup_form_row( + table_name: &str, header: &str, allow_delete: &Option, allowed_values: &Option>, @@ -1904,17 +1904,22 @@ fn get_hiccup_form_row( select_element.insert(2, json!(["option", {"value": "", "selected": true}])); } value_col.push(json!(select_element)); - } else if vec!["text", "number", "search"].contains(&html_type) { + } else if vec!["text", "number", "search", "multisearch"].contains(&html_type) { // TODO: This html type will need to be re-implemented (later). // TODO: Support a range restriction for 'number' classes.insert(0, "form-control"); input_attrs.insert("type".to_string(), json!(html_type)); - if html_type == "search" { + if ["search", "multisearch"].contains(&html_type) { classes.append(&mut vec!["search", "typeahead"]); + if html_type == "multisearch" { + classes.push("multiple"); + } input_attrs.insert( "id".to_string(), json!(format!("{}-typeahead-form", header)), ); + input_attrs.insert("data-table".to_string(), json!(table_name)); + input_attrs.insert("data-column".to_string(), json!(header)); } input_attrs.insert("class".to_string(), json!(classes.join(" "))); match value {