diff --git a/README.md b/README.md index 5f34f72ed02f9c23ef3c642a0a496b286dbe04af..17dc0362ba2759b15fe9d51121f9c6de3be1d55d 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,45 @@ -# Search box component +# Kldb Search box component -`<search-box>` displays a search box similar to the one you could find on your knowledge base instance +`<kldb-search-box>` displays a search box similar to the one you could find on your knowledge base instance. -```html -<search-box /> -``` +*Warning* +This component is used to filter results of another component : [`<kldb-search-results>`](https://git.startinblox.com/applications/knowledge-base/components/search-results). You need to implement both components and link them with a script. + + +> When we made this component we thought that it was relevant to separate it in two parts : the filters and the results in order to get more freedom in the implementation. So we've added a script to link them. We are looking for a smarter solution. If you have any suggestions for improvement, we would be happy to hear from you :) + + +## Try the demo + +To launch the demo, please run at the root of the component : -## Installation -First install the knowledge base wordpress plugin. +```console +npm run serve +``` ## How to use -Once the package is installed, you can use the `search-box` component: + ```html <html> <head> - <!-- import the module in the head of the page --> - <script type="module" src="https://unpkg.com/@coopstarter/search-box"></script> + <!-- import the Kldb Search Box component --> + <script type="module" src="https://unpkg.com/@startinblox/kldb-search-box"></script> + <!-- import the Kldb Result Search component --> + <script type="module" src="https://unpkg.com/@startinblox/kldb-search-results"></script> + <!-- import the script to connect the two components --> + <script src="https://unpkg.com/@startinblox/kldb-search-box@0.1.1/js/connector-script.js"></script> </head> <body> - <!-- use the component --> - <search-box + <!-- Use Kldb Search Box component --> + <kldb-search-box + data-src="[url]"> + </kldb-search-box> + + <!-- Use Kldb Result Search component --> + <kldb-search-results data-src="[url]"> - </search-box> + </kldb-search-results> </body> </html> ``` @@ -31,23 +48,17 @@ Once the package is installed, you can use the `search-box` component: | Name | Default | Description | | --------------- | ---------------------- | :----------------------------------------------------------- | -| `data-src` | `undefined` | URL of the LDP conversation(s) to display. +| `data-src` | `undefined` | URL of source to display. For exemple : https://api.coopstarter.happy-dev.fr -### Design -To customize your component, you can set the css variable `--sib-conversation-theme` anywhere in your stylesheet to the color you want. -For example: -```css -:root{ - --search-box-theme: #1abba6; -} -``` -## Output -* On the top, a textarea to create a conversation -* Then, a list of conversations, or main posts, ordered anti chronologically -* Each conversation is directly followed by a list of messages related to that conversation. These messages are also ordered chronologically -* At the bottom of each list of comments, a textarea allows one to add a comment and participate to the discussion -* For each conversation and message, sib-conversation displays the avatar of this author, her name as well as the amount of time since the info was posted +## Notes +This component was made for The [Coopstarter projet](http://starter.coop/wp/) by Balessan and Alice. + +## Funding + + +This software has been co-funded by the European Union. +The contents of this software are the sole responsibility of Cooperatives Europe and can in no way be taken to reflect the views of the European Union. diff --git a/documentation/.gitkeep b/documentation/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/documentation/EU_logos.png b/documentation/EU_logos.png new file mode 100644 index 0000000000000000000000000000000000000000..b4c55ee75d059d4d6bb213f29d32d7b218ccf9af Binary files /dev/null and b/documentation/EU_logos.png differ diff --git a/index.html b/index.html index 59c7113019e8080503ef4a15325e0b1b465847dd..35986be66d646e32793477a6474f7a0d2d5c2f3e 100644 --- a/index.html +++ b/index.html @@ -3,9 +3,11 @@ <head> <meta charset="UTF-8"> <title>Knowledge base Search Box - Demo</title> - <script src="https://kit.fontawesome.com/48014d2af3.js"></script> <script type="module" src="search-box.js"></script> + <script type="module" src="https://unpkg.com/@startinblox/kldb-search-results"></script> + <script src="js/connector-script.js"></script> + </head> <body> @@ -15,8 +17,11 @@ </h2> <kldb-search-box - data-src="http://localhost:8000"> + data-src="https://api.coopstarter.happy-dev.fr"> </kldb-search-box> + <kldb-search-results + data-src="https://api.coopstarter.happy-dev.fr"> + </kldb-search-results> </div> </body> </html> diff --git a/js/connector-script.js b/js/connector-script.js new file mode 100644 index 0000000000000000000000000000000000000000..bc35d4d1e90aa14419a7b1f631a11c1ba3251775 --- /dev/null +++ b/js/connector-script.js @@ -0,0 +1,406 @@ +//This script is made to connect the search-box and the box-result. + +/** + * Manage classic tabs. + * @param {string} pageName - Id of the tab content + * @param {HTMLElement} elmnt - Accordion element + */ +function openTab(pageName, elmnt) { + // Hide all elements with class="tabcontent" by default */ + var i, tabcontent, tablinks; + tabcontent = document.getElementsByClassName("tabcontent"); + tablinks = document.getElementsByClassName("tablink"); + + for (i = 0; i < tabcontent.length; i++) { + tabcontent[i].style.display = "none"; + tablinks[i].classList.remove("active"); + } + + // Show the specific tab content + document.getElementById(pageName).style.display = "block"; + elmnt.classList.add("active"); +} + +/** + * Remove pagination when there is no resource in a step group. + */ +function refreshPagination() { + var resources_containers = document.querySelectorAll( + ".resource_by_step sib-form+div" + ); + + for (let resources_container of resources_containers) { + if ( + resources_container.nextSibling && + (resources_container.childElementCount < 5 || + resources_container.nextSibling.querySelector("nav span span+span") + .textContent == 1) + ) { + resources_container.nextSibling.setAttribute("style", "display:none"); + } else if ( + resources_container.nextSibling && + resources_container + .closest(".step") + .querySelector(".accordion:not(.active)") + ) { + resources_container.nextSibling.setAttribute("hidden", "hidden"); + resources_container.nextSibling.setAttribute("style", "display:none"); + } else { + resources_container.nextSibling.setAttribute("style", "display:flex"); + } + } +} + +/** + * Manage the visual of the fake tabs in entrepreneur dashboard. + * @param {HTMLElement} elmnt - Active fake tab. + */ +function openFakeTab(elmnt) { + // Hide all elements with class="tabcontent" by default */ + var i, tablinks; + tablinks = document.getElementsByClassName("tablink"); + + for (i = 0; i < tablinks.length; i++) { + tablinks[i].classList.remove("active"); + } + + // Show the specific tab content + elmnt.classList.add("active"); +} + +/** + * Manage the accordion for step in entrepreneur dashboard + */ +function manageAccordionByStep() { + var acc = document.getElementsByClassName("accordion"); + var i; + + for (i = 0; i < acc.length; i++) { + acc[i].addEventListener("click", function () { + if (this.classList.contains("active") == true) { + this.classList.remove("active"); + this.nextElementSibling.querySelector( + "sib-form + div" + ).style.maxHeight = "0px"; + } else { + this.classList.add("active"); + } + + this.closest(".step") + .querySelector("nav") + .removeAttribute("hidden"); + refreshPagination(); + + var panel = this.nextElementSibling.querySelector("sib-form + div"); + + if (panel.style.maxHeight) { + panel.style.maxHeight = null; + } else { + panel.style.maxHeight = panel.scrollHeight + "px"; + } + }); + } +} + +/** + * Manage select hidden to fullfill them with more "more criterias" selection + * @param {HTMLElement} select_hidden - Hidden select to fullfill. + * @param {HTMLElement} option_selected - Option selcted to set in hidden select. + */ +function selectHiddenManagement(select_hidden, option_selected) { + options_hidden = select_hidden.getElementsByTagName("option"); + for (let option_hidden of options_hidden) { + //Removing the selected attribute from previous selection + option_hidden.removeAttribute("selected"); + + if (option_hidden.value == option_selected.value) { + //Actually selecting the option if it is the good one + option_hidden.setAttribute("selected", "selected"); + select_hidden.setAttribute("value", option_selected.value); + } + } + + //Trigerring a reload of the associated form + let parent_form = select_hidden.closest("sib-form"); + parent_form.component.inputChange(); + refreshPagination(); + }; + + + +/** + * Manage input hidden field to fullfill them with more "more criterias" selection + * @param {HTMLElement} field - Hidden field to fullfill. + * @param {HTMLElement} field_search - Field with the value wanted by the user. + */ +function inputHiddenManagement(field, field_search) { + field.setAttribute("value", field_search.value); + let parent_form = field.closest("sib-form"); + parent_form.component.inputChange(); + refreshPagination(); +} + +/** + * Manage select hidden for type to fullfill them with more "more criterias" selection + * @param {HTMLElement} tab - Selected type tabs. + * @param {HTMLElement} form - Hidden form to fullfill. + */ +function selectHiddenManagementForType(tab, form) { + let type_hidden_field = form.querySelectorAll( + 'hidden-widget[name="more_criterias_hidden"] select[name="type"]' + ); + + if (tab.classList.contains("active")) { + //Fullfill hidden field + let type_field_search = tab + .querySelector(`sib-display`) + .getAttribute("data-src"); + + type_hidden_field.forEach(function(select_hidden) { + options_hidden = select_hidden.getElementsByTagName("option"); + for (let option_hidden of options_hidden) { + option_hidden.removeAttribute("selected"); + if (option_hidden.value == '{"@id": "' + type_field_search + '"}') { + //Actually selecting the option if it is the good one + option_hidden.setAttribute("selected", "selected"); + select_hidden.setAttribute("value", option_hidden.value); + } + } + + //Trigerring a reload of the associated form + let parent_form = select_hidden.closest("sib-form"); + parent_form.component.inputChange(); + refreshPagination(); + }); + } +} + +/** + * Init type to type 1 + */ +function InitManagementForType() { + var forms = document.querySelectorAll(".resource_by_step"); + + forms.forEach(form => { + form.addEventListener( + "populate", + e => { + window.setTimeout(() => { + let type_hidden_field = form.querySelectorAll('select[name="type"]'); + type_hidden_field.forEach(function(select_hidden) { + options_hidden = select_hidden.getElementsByTagName("option"); + for (let option_hidden of options_hidden) { + option_hidden.removeAttribute("selected"); + if (option_hidden.value == '{"@id": "http://localhost:8000/types/1/"}') { + //Actually selecting the option if it is the good one + option_hidden.setAttribute("selected", "selected"); + select_hidden.setAttribute("value", option_hidden.value); + } + } + //Trigerring a reload of the associated form + let parent_form = select_hidden.closest("sib-form"); + parent_form.component.inputChange(); + refreshPagination(); + }); + }); + }, + 3000 + ); + }); +} + + +/** + * For entrepreneur dashboard only : + * As we cannot have multiple imbricated filtering with the native sib-display, we manage it manually. + * @param {String} resultsBlockId - Id of the element to update + * @param {String} searchBlockId - Id of the filter block element + */ +function addProperFilterToSearchComponents(resultsBlockId, searchBlockId) { + var baseElement = document.getElementById(resultsBlockId); + var searchBaseElement = document.getElementById(searchBlockId); + var forms = baseElement.querySelectorAll(".resource_by_step"); + forms.forEach(form => { + form.addEventListener("populate", e => { + //Manage fake tabs + let tabs = baseElement.getElementsByClassName("filter_by_type"); + for (let tab of tabs) { + selectHiddenManagementForType(tab, form); + } + + //Manage the pagination + refreshPagination(); + + //SEARCH BY KEYWORD + //To retrieve keyword + //https://git.happy-dev.fr/startinblox/framework/sib-core/issues/379 + //TODO: Wait for a solution to filter with multiple value with "OR" instead of "AND". + let keyword_form = searchBaseElement.querySelectorAll(".search-by-keyword")[0]; + if (keyword_form) { + let keyword_field = keyword_form.querySelector( + `input[name="name_keyword"]` + ); + + if (keyword_field) { + let keyword_submit = searchBaseElement.querySelectorAll(" .keyword_submit")[0]; + let keyword_hidden_fields = baseElement.querySelectorAll( + 'hidden-widget[name="search_for_a_resource"] input' + ); + + keyword_submit.addEventListener("click", function () { + keyword_hidden_fields.forEach(hidden_field => { + hidden_field.setAttribute("value", keyword_field.value); + hidden_field.value = keyword_field.value; + + let parent_form = hidden_field.closest("sib-form"); + parent_form.component.inputChange(); + refreshPagination(); + }); + }); + } + } + + //SEARCH IN DATABASE INSTANCE ONLY + let instance_only = searchBaseElement.querySelectorAll( + ".instance_database_only" + )[0]; + let checkbox_instance_only = instance_only.querySelector("input"); + if (checkbox_instance_only) { + checkbox_instance_only.onclick = function () { + if (this.checked) { + //We have to retrieve all data-scr needed to make them pointed only on the instance. + //How to? + } + }; + } + + //MORE CRITERIAS + const more_criterias_form = searchBaseElement.querySelectorAll( + ".more_criterias" + )[0]; + + // https://git.happy-dev.fr/startinblox/framework/sib-core/issues/453 + window.setTimeout(() => { + //Manage fake tabs for type + let tabs = baseElement.getElementsByClassName("filter_by_type"); + for (let tab of tabs) { + tab.addEventListener("click", function () { + selectHiddenManagementForType(tab, form); + }); + } + + //To retrieve format + let format_field_search = more_criterias_form.querySelector( + `select[name="format"]` + ); + let format_hidden_field = baseElement.querySelectorAll( + 'hidden-widget[name="more_criterias_hidden"] select[name="format"]' + ); + + //TODO: The first time the event is not call. + if (format_field_search) { + format_field_search.onchange = function () { + let option_selected = format_field_search.querySelector( + "option:checked" + ); + + format_hidden_field.forEach(function (select_hidden) { + selectHiddenManagement(select_hidden, option_selected); + }); + }; + } + + //To retrieve language + let language_field_search = more_criterias_form.querySelector( + `select[name="language"]` + ); + language_hidden_field = baseElement.querySelectorAll( + 'hidden-widget[name="more_criterias_hidden"] select[name="language"]' + ); + + if (language_field_search) { + language_field_search.onchange = function () { + let option_selected = language_field_search.querySelector( + "option:checked" + ); + + language_hidden_field.forEach(function (select_hidden) { + selectHiddenManagement(select_hidden, option_selected); + }); + }; + } + + //To retrieve field + let field_field_search = more_criterias_form.querySelector( + `select[name="fields"]` + ); + let field_hidden_field = baseElement.querySelectorAll( + 'hidden-widget[name="more_criterias_hidden"] select[name="fields"]' + ); + + if (language_field_search) { + field_field_search.onchange = function () { + let option_selected = field_field_search.querySelector( + "option:checked" + ); + + field_hidden_field.forEach(function (select_hidden) { + selectHiddenManagement(select_hidden, option_selected); + }); + }; + } + + //To retrieve year of publication + //WARNING: If the user want to select "20" to get 21century made, he will get no result. + //I think it is a UX problem. + let year_field_search = more_criterias_form.querySelector( + `input[name="publication_year"]` + ); + let year_hidden_fields = baseElement.querySelectorAll( + 'hidden-widget[name="more_criterias_hidden"] input[name="publication_year"]' + ); + + if (year_field_search) { + year_field_search.addEventListener("input", function () { + year_hidden_fields.forEach(field => { + inputHiddenManagement(field, year_field_search, form); + }); + }); + } + + //To retrieve the country + let country_field_search = more_criterias_form.querySelector( + `input[name="country"]` + ); + let country_hidden_fields = baseElement.querySelectorAll( + 'hidden-widget[name="more_criterias_hidden"] input[name="country"]' + ); + + if (country_field_search) { + country_field_search.addEventListener("input", function () { + country_hidden_fields.forEach(field => { + inputHiddenManagement(field, country_field_search, form); + }); + }); + } + }, 1000); + }); + }); +} + + +/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXX ON LOAD XXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/ + +//This is a trick to avoid Jquery dependance to replace 'ready' method. +function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()} + +r(function(){ + refreshPagination(); + addProperFilterToSearchComponents("kldb-search-results-component", "kldb-search-box-component"); +}); + + +