From 9f9bf11707f61ff8f5fffcc75e150ddbbdd6ba79 Mon Sep 17 00:00:00 2001 From: KL-yamada Date: Thu, 27 Oct 2022 10:40:24 +0900 Subject: [PATCH 1/4] =?UTF-8?q?JDCAT=202022/11=20=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app-facet-search/src/App.js | 39 ++- .../src/components/RangeCheckboxList.jsx | 255 ++++++++++++++++++ .../src/components/RangeFacet.jsx | 18 +- app-facet-search/src/index.css | 90 +++++++ 4 files changed, 385 insertions(+), 17 deletions(-) create mode 100644 app-facet-search/src/components/RangeCheckboxList.jsx create mode 100644 app-facet-search/src/index.css diff --git a/app-facet-search/src/App.js b/app-facet-search/src/App.js index e0c28f7..7d752cf 100644 --- a/app-facet-search/src/App.js +++ b/app-facet-search/src/App.js @@ -1,5 +1,6 @@ import 'rc-slider/assets/index.css'; import 'rc-tooltip/assets/bootstrap.css'; +import './index.css'; import React from "react"; import fetch from "unfetch"; import RangeFacet from "./components/RangeFacet"; @@ -15,10 +16,13 @@ class FacetSearch extends React.Component { constructor(props) { super(props); this.state = { - is_enable: true, + is_enable: false, list_title: {}, list_facet: {}, - list_order: {} + list_order: {}, + list_uiType: {}, + list_isOpen: {}, + list_displayNumber: {} }; this.getTitleAndOrder = this.getTitleAndOrder.bind(this); this.get_facet_search_list = this.get_facet_search_list.bind(this); @@ -28,15 +32,25 @@ class FacetSearch extends React.Component { getTitleAndOrder() { let titleLst = {}; let orderLst = {}; + let uiTypeLst = {}; + let isOpenLst = {}; + let displayNumberLst = {}; fetch("/facet-search/get-title-and-order", {method: "POST"}) .then((r) => r.json()) .then((response) => { if (response.status) { titleLst = response.data.titles; - orderLst =response.data.order; + orderLst = response.data.order; + uiTypeLst = response.data.uiTypes; + isOpenLst = response.data.isOpens; + displayNumberLst = response.data.displayNumbers; } this.setState({ list_title: titleLst }); this.setState({ list_order: orderLst }); + this.setState({ list_uiType: uiTypeLst }); + this.setState({ list_isOpen: isOpenLst }); + this.setState({ list_displayNumber: displayNumberLst }); + this.setState({ is_enable: true }); }); } @@ -77,9 +91,13 @@ class FacetSearch extends React.Component { let a = tmp.buckets[i]; if ((l == "en") && ((a.key).charCodeAt(0) > 256 || (a.key).charCodeAt(a.key.length - 1) > 256)) { - delete list_facet[name].buckets[i]; + //delete list_facet[name].buckets[i]; + list_facet[name].buckets.splice(i,1); + i--; } else if ((l != "en") && ((a.key).charCodeAt(0) < 256 && (a.key).charCodeAt(a.key.length - 1) < 256)) { - delete list_facet[name].buckets[i]; + //delete list_facet[name].buckets[i]; + list_facet[name].buckets.splice(i,1); + i--; } } } @@ -90,7 +108,9 @@ class FacetSearch extends React.Component { let a = tmp.buckets[i]; if (((a.key).charCodeAt(0) > 256 || (a.key).charCodeAt(a.key.length - 1) > 256)) { - delete list_facet[name].buckets[i]; + //delete list_facet[name].buckets[i]; + list_facet[name].buckets.splice(i,1); + i--; } } } @@ -107,7 +127,7 @@ class FacetSearch extends React.Component { } render() { - const { is_enable, list_title, list_facet, list_order } = this.state; + const { is_enable, list_title, list_facet, list_order, list_uiType, list_isOpen, list_displayNumber } = this.state; return (
{is_enable && ( @@ -116,8 +136,11 @@ class FacetSearch extends React.Component { const name = list_order[order]; const item = list_facet[name]; const nameshow = list_title[name]; + const isOpen = list_isOpen[name]; + const uiType = list_uiType[name]; + const displayNumber = list_displayNumber[name]; return ( - + ); })}
diff --git a/app-facet-search/src/components/RangeCheckboxList.jsx b/app-facet-search/src/components/RangeCheckboxList.jsx new file mode 100644 index 0000000..8fddcf6 --- /dev/null +++ b/app-facet-search/src/components/RangeCheckboxList.jsx @@ -0,0 +1,255 @@ +import "rc-slider/assets/index.css"; +import "rc-tooltip/assets/bootstrap.css"; +import React from "react"; + + +/** + * A UI component that displays faceted items as a list of checkboxes. + * This part consists of a list portion and a modal portion. + * + * + * The List portion displays a list of checkboxes with the number of displayNumber + * set in the admin panel. If the number of faceted items is greater than the + * number of displayNumber, a link to display the Modal is displayed. + * + * The checkboxes displayed in the List section are narrowed down by facet item + * at the same time as Click is performed. + * + * + * + * In the modal section, all faceted items are displayed in a modal. + * Since scrolling is used, there is no limit to the number of items. + * The number of display columns is also changed according to the screen size to be displayed. + * + * In the modal portion, no narrowing is performed until the search button is pressed. + * Multiple items can be selected and narrowed down in a batch. + * + * The modal portion can be closed by pressing the Cancel button or clicking + * on the portion outside the modal. The facet items that were selected before + * the refinement are cleared when the modal is closed. + * + * @param {array} values An array consisting of faceted item names (key) and the number of items in the target (doc_count). + * @param {string} name English name of facet item. + * @param {array} labels Array of labels used in translation. + * @param {integer} displayNumber Number of items displayed in the list. + * + * @author knowledge labo yamada + */ +function RangeCheckboxList({ values, name, labels, displayNumber }) { + + /** + * Returns the DOM representing the checkbox. + * + * @param {string} id ID of the checkbox. + * @param {string} value Value of the checkbox. + * @param {boolean} checked The selected state of the checkbox. + * @param {function} onChange Process when the check box is clicked, which is set only when List is displayed. + * @returns DOM representing a checkbox. + */ + const CheckBox = ({ id, value, checked, onChange}) => { + if(onChange !=null) { + //for lists + return ( + + ) + } else { + //for Modal + return ( + + ) + } + + } + + /** + * Returns the DOM of a list of checkboxes. + * This function is used for both List and Modal. The parameter isModal controls which use is made of this function. + * + * @param {array} values An array consisting of faceted item names (key) and the number of items in the target (doc_count). + * @param {string} name English name of facet item. + * @param {bool} isModal True for modal use. false for list use. + * @param {integer} displayNumber Number of items displayed in the list. + * @param {function} onChange Process when the check box is clicked, which is set only when List is displayed. + * @returns DOM representing a checkbox list. + */ + const CheckBoxList = ({ values, name, isModal, displayNumber, onChange}) => { + return ( + values.map((subitem,index) => { + if (isModal || index < displayNumber) { + let id = "id_" + name + (isModal ? "_chkbox_mdl_" : "_chkbox_") + index; + let label = subitem.key + "(" + subitem.doc_count + ")"; + let checked = params.indexOf(name + "=" + subitem.key)!= -1; + return ( +
+ +
+ ) + } + }) + ) + } + + /** + * Returns the modal DOM. + * + * @param {array} values An array consisting of faceted item names (key) and the number of items in the target (doc_count). + * @param {string} name English name of facet item. + * @param {bool} modalId ID set for the modal. + * @returns Modal DOM + */ + const ModalCheckboxList = ({ values, name, modalId }) => { + return ( +
+ +
+
+
+ +
+
+ {labels['cancel']} + +
+
+
+
+ ) + }; + + // Show/Hide Modal + var status = 'Hide'; + + /** + * Called to close the modal. + */ + function closeModal(){ + status = 'Hide'; + } + + /** + * Called to open a modal. + * Reconfigure it to select only the checkboxes that have been narrowed down + * from the parameters of the URL at the time before displaying. + */ + function openModal(){ + status = 'Show'; + document.querySelectorAll('.chbox-mdl input').forEach(el => { + el.checked = params.indexOf(name + "=" + el.value)!= -1; + }); + } + + /** + * Processing when a check box is selected when the List is displayed. The search items of + * the newly selected check box are added to the parameter to narrow down the search. + */ + function handleListChange(e) { + const targets = []; + document.querySelectorAll('.chbox-mdl input').forEach(el => { + if((el.checked && e.target.value !== el.value) || (e.target.checked && e.target.value === el.value)){ + targets.push({label: name, value: el.value}); + } + }); + executeSearch(targets); + } + + /** + * When the search button is pressed during modal display, a callout difference srere. + * The search item for the newly selected check box is added to the parameters and a narrowed search is performed. + */ + function handleModalListChange(e) { + const targets = []; + document.querySelectorAll('.chbox-mdl input').forEach(el => { + if(el.checked){ + targets.push({label: name, value: el.value}); + } + }); + executeSearch(targets); + } + + /** + * Narrowing is performed based on the narrowing target specified in this facet item. + * Parameters other than this facet item are used as is. + * + * @param {array} targets Parameters to be refined by this facet item. + */ + function executeSearch(targets) { + let searchUrl = ""; + if (search.indexOf("&") >= 0) { + let arrSearch = search.split("&"); + for (let i = 0; i < arrSearch.length; i++) { + //Parameters other than this facet item are used as is. + if (arrSearch[i].indexOf(encodeURIComponent(name) + "=") < 0) { + searchUrl += "&" + arrSearch[i]; + } + } + //Delete "&" in First element + searchUrl = searchUrl.substring(1); + } + if (searchUrl != "") { + search = searchUrl; + } + targets.map(function (subitem, k) { + const pattern = + encodeURIComponent(name) + "=" + encodeURIComponent(subitem.value); + search += "&" + pattern; + }); + search = search.replace("q=0", "q="); + search += search.indexOf('is_facet_search=') == -1 ? '&is_facet_search=true' : ''; + window.location.href = "/search" + search; + } + + let search = window.location.search.replace(",", "%2C") || "?"; + let params = window.location.search.substring(1).split('&'); + for (let i = 0; i < params.length; i++) { + params[i] = decodeURIComponent(params[i]); + } + let modalId = "id_" + name + "_checkbox_modal"; + var timeoutId ; + + /* + * Responsive measures. + * Addresses the problem of hiding the modal at the timing when the style is changed + * for mobile when the Window size is reduced. When resizing, prompt to redisplay if in display state. + */ + window.addEventListener("resize", function() { + if(status==='Hide'){ + return; + } + if ( timeoutId ) return ; + timeoutId = setTimeout( function () { + timeoutId = 0 ; + window.location.hash='#' + modalId; + }, 100 ) ; + }); + + let dp = displayNumber == null ? 5 : displayNumber; + return ( +
+
+ + {values.length > dp && + . . . See More + } + +
+
+ ); +} + +export default RangeCheckboxList; diff --git a/app-facet-search/src/components/RangeFacet.jsx b/app-facet-search/src/components/RangeFacet.jsx index 2112b67..ba194ab 100644 --- a/app-facet-search/src/components/RangeFacet.jsx +++ b/app-facet-search/src/components/RangeFacet.jsx @@ -4,21 +4,18 @@ import React, { useState } from "react"; import { Collapse } from "reactstrap"; import RangeSelect from "./RangeSelect"; import RangeSlider from "./RangeSlider"; +import RangeCheckboxList from "./RangeCheckboxList"; -function check_temp(name) { - return name === "Time Period(s)"; -} - -function RangeFacet({ item, nameshow, name, key, labels }) { +function RangeFacet({ item, nameshow, name, key, labels, isInitOpen, uiType, displayNumber }) { const toggle = () => setIsOpen(!isOpen); const search = window.location.search.replace(",", "%2C"); - const is_check = search.indexOf(encodeURIComponent(name)) >= 0 ? true : false; + const is_check = search.indexOf(encodeURIComponent(name)) >= 0 ? true : isInitOpen; const [isOpen, setIsOpen] = useState(is_check); return (

{nameshow}

- + {!isOpen && ( @@ -33,10 +30,13 @@ function RangeFacet({ item, nameshow, name, key, labels }) {
- {!check_temp(name) && ( + {item != null && uiType === "SelectBox" && ( )} - {check_temp(name) && ( + {item != null && uiType === "CheckboxList" && ( + + )} + {item != null && uiType === "RangeSlider" && ( )}
diff --git a/app-facet-search/src/index.css b/app-facet-search/src/index.css new file mode 100644 index 0000000..3c04939 --- /dev/null +++ b/app-facet-search/src/index.css @@ -0,0 +1,90 @@ +.chbox-container { + margin-left:10px; + display:table; + width:100%; +} + +.chbox-container label{ + user-select: none; + +} + +.chbox-mdl { + z-index: 999; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 40px 10px; + text-align: center; +} + +.chbox-mdl:not(:target) { + opacity: 0; + visibility: hidden; + transition: opacity .3s, visibility .3s; +} + +.chbox-mdl:target { + opacity: 1; + visibility: visible; + transition: opacity .4s, visibility .4s; +} + +.chbox-mdl::after { + display: inline-block; + height: 100%; + margin-left: -.05em; + vertical-align: middle; + content: "" +} + +.chbox-mdl .overlay { + z-index: 10; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0, 0, 0, .8) +} + +.chbox-mdl .window { + box-sizing: border-box; + display: inline-block; + z-index: 20; + position: relative; + width: 90%; + min-width: 320px; + border-radius: 2px; + background: #fff; + box-shadow: 0 0 30px rgba(0, 0, 0, .6); + vertical-align: middle +} + +.chbox-mdl .window .content { + max-height: 80vh; + overflow-y: auto; + text-align: left; + padding: 20px; + line-height: 15px +} + +.chbox-mdl .window .content .list { + display: grid; + gap: 10px; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + max-height: 50vh; + overflow-y: scroll; +} + +.chbox-mdl .window .content .footer { + width: 100%; + text-align: center; + padding-top: 20px; +} + +.chbox-mdl .window .content .footer button { + margin-left: 50px; +} \ No newline at end of file From 405eec49a524328f136615fc70a3ee59ba040f9a Mon Sep 17 00:00:00 2001 From: KL-yamada Date: Thu, 24 Nov 2022 13:06:47 +0900 Subject: [PATCH 2/4] Fixing test defects --- .../src/components/RangeCheckboxList.jsx | 42 +++++++------------ app-facet-search/src/index.css | 14 +++++-- cp_app_facet_search.sh | 5 ++- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/app-facet-search/src/components/RangeCheckboxList.jsx b/app-facet-search/src/components/RangeCheckboxList.jsx index 8fddcf6..571bca4 100644 --- a/app-facet-search/src/components/RangeCheckboxList.jsx +++ b/app-facet-search/src/components/RangeCheckboxList.jsx @@ -116,14 +116,14 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { const ModalCheckboxList = ({ values, name, modalId }) => { return (
- +
- {labels['cancel']} + {labels['cancel']}
@@ -132,14 +132,14 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { ) }; - // Show/Hide Modal - var status = 'Hide'; - /** * Called to close the modal. */ - function closeModal(){ - status = 'Hide'; + function closeModal(e){ + if(e == null){ + return; + } + document.getElementById(e.target.getAttribute('modalId')).classList.remove("is-active"); } /** @@ -147,8 +147,13 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { * Reconfigure it to select only the checkboxes that have been narrowed down * from the parameters of the URL at the time before displaying. */ - function openModal(){ - status = 'Show'; + function openModal(e){ + + if(e == null){ + console.log("event == null" ); + return; + } + document.getElementById(e.target.getAttribute('modalId')).classList.add("is-active"); document.querySelectorAll('.chbox-mdl input').forEach(el => { el.checked = params.indexOf(name + "=" + el.value)!= -1; }); @@ -220,23 +225,6 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { params[i] = decodeURIComponent(params[i]); } let modalId = "id_" + name + "_checkbox_modal"; - var timeoutId ; - - /* - * Responsive measures. - * Addresses the problem of hiding the modal at the timing when the style is changed - * for mobile when the Window size is reduced. When resizing, prompt to redisplay if in display state. - */ - window.addEventListener("resize", function() { - if(status==='Hide'){ - return; - } - if ( timeoutId ) return ; - timeoutId = setTimeout( function () { - timeoutId = 0 ; - window.location.hash='#' + modalId; - }, 100 ) ; - }); let dp = displayNumber == null ? 5 : displayNumber; return ( @@ -244,7 +232,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) {
{values.length > dp && - . . . See More + . . . See More }
diff --git a/app-facet-search/src/index.css b/app-facet-search/src/index.css index 3c04939..17a37b9 100644 --- a/app-facet-search/src/index.css +++ b/app-facet-search/src/index.css @@ -18,15 +18,12 @@ left: 0; padding: 40px 10px; text-align: center; -} - -.chbox-mdl:not(:target) { opacity: 0; visibility: hidden; transition: opacity .3s, visibility .3s; } -.chbox-mdl:target { +.chbox-mdl.is-active { opacity: 1; visibility: visible; transition: opacity .4s, visibility .4s; @@ -87,4 +84,13 @@ .chbox-mdl .window .content .footer button { margin-left: 50px; +} + +.rc-slider-mark-text { + display: none !important; +} + +.rc-slider-dot { + border: initial !important; + background-color: initial !important; } \ No newline at end of file diff --git a/cp_app_facet_search.sh b/cp_app_facet_search.sh index 72507d0..30491a6 100755 --- a/cp_app_facet_search.sh +++ b/cp_app_facet_search.sh @@ -17,8 +17,11 @@ fi TARGETDIR=$WEKODIR/modules # args-check-end +# Merging Style Files +cat ./app-facet-search/build/static/css/2.*chunk.css ./app-facet-search/build/static/css/main.*.chunk.css > ./app-facet-search/build/static/css/facet_chunk.css + # copy-begin -cp -p ./app-facet-search/build/static/css/*.chunk.css ${TARGETDIR}/weko-search-ui/weko_search_ui/static/css/weko_search_ui/facet_chunk.css +cp -p ./app-facet-search/build/static/css/facet_chunk.css ${TARGETDIR}/weko-search-ui/weko_search_ui/static/css/weko_search_ui/facet_chunk.css cp -p ./app-facet-search/build/static/js/main.*.chunk.js ${TARGETDIR}/weko-search-ui/weko_search_ui/static/js/weko_search_ui/facet.main.chunk.js cp -p ./app-facet-search/build/static/js/runtime-main.*.js ${TARGETDIR}/weko-search-ui/weko_search_ui/static/js/weko_search_ui/facet.runtime-main.js cp -p ./app-facet-search/build/static/js/2.*.chunk.js ${TARGETDIR}/weko-search-ui/weko_search_ui/static/js/weko_search_ui/facet.chunk.js From 146ed01c6a6af7f2aac84cc832e8c7bdc0d5389a Mon Sep 17 00:00:00 2001 From: KL-yamada Date: Mon, 19 Dec 2022 22:51:12 +0900 Subject: [PATCH 3/4] bug fix #35026 --- .../src/components/RangeCheckboxList.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app-facet-search/src/components/RangeCheckboxList.jsx b/app-facet-search/src/components/RangeCheckboxList.jsx index 571bca4..53bf763 100644 --- a/app-facet-search/src/components/RangeCheckboxList.jsx +++ b/app-facet-search/src/components/RangeCheckboxList.jsx @@ -37,6 +37,10 @@ import React from "react"; */ function RangeCheckboxList({ values, name, labels, displayNumber }) { + //If there is a space in the id attribute, it cannot be searched by ID, so escape it. + let facet_item_id = "id_" + name + "_chkbox"; + let facet_item_id_for_search = facet_item_id.replace(' ', '\\ '); + /** * Returns the DOM representing the checkbox. * @@ -154,7 +158,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { return; } document.getElementById(e.target.getAttribute('modalId')).classList.add("is-active"); - document.querySelectorAll('.chbox-mdl input').forEach(el => { + document.querySelector('#' + facet_item_id_for_search).querySelectorAll('.chbox-mdl input').forEach(el => { el.checked = params.indexOf(name + "=" + el.value)!= -1; }); } @@ -165,7 +169,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { */ function handleListChange(e) { const targets = []; - document.querySelectorAll('.chbox-mdl input').forEach(el => { + document.querySelector('#' + facet_item_id_for_search).querySelectorAll('.chbox-mdl input').forEach(el => { if((el.checked && e.target.value !== el.value) || (e.target.checked && e.target.value === el.value)){ targets.push({label: name, value: el.value}); } @@ -179,7 +183,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { */ function handleModalListChange(e) { const targets = []; - document.querySelectorAll('.chbox-mdl input').forEach(el => { + document.querySelector('#' + facet_item_id_for_search).querySelectorAll('.chbox-mdl input').forEach(el => { if(el.checked){ targets.push({label: name, value: el.value}); } @@ -228,7 +232,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { let dp = displayNumber == null ? 5 : displayNumber; return ( -
+
{values.length > dp && From a6f3d088574e1820469be80594f4b7e88bc642df Mon Sep 17 00:00:00 2001 From: KL-yamada Date: Mon, 19 Dec 2022 22:51:12 +0900 Subject: [PATCH 4/4] bug fix #35026 --- .../src/components/RangeCheckboxList.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app-facet-search/src/components/RangeCheckboxList.jsx b/app-facet-search/src/components/RangeCheckboxList.jsx index 571bca4..32b5926 100644 --- a/app-facet-search/src/components/RangeCheckboxList.jsx +++ b/app-facet-search/src/components/RangeCheckboxList.jsx @@ -37,6 +37,10 @@ import React from "react"; */ function RangeCheckboxList({ values, name, labels, displayNumber }) { + //If there is a space in the id attribute, it cannot be searched by ID, so escape it. + let facet_item_id = "id_" + name + "_chkbox"; + let facet_item_id_for_search = CSS.escape(facet_item_id); + /** * Returns the DOM representing the checkbox. * @@ -154,7 +158,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { return; } document.getElementById(e.target.getAttribute('modalId')).classList.add("is-active"); - document.querySelectorAll('.chbox-mdl input').forEach(el => { + document.querySelector('#' + facet_item_id_for_search).querySelectorAll('.chbox-mdl input').forEach(el => { el.checked = params.indexOf(name + "=" + el.value)!= -1; }); } @@ -165,7 +169,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { */ function handleListChange(e) { const targets = []; - document.querySelectorAll('.chbox-mdl input').forEach(el => { + document.querySelector('#' + facet_item_id_for_search).querySelectorAll('.chbox-mdl input').forEach(el => { if((el.checked && e.target.value !== el.value) || (e.target.checked && e.target.value === el.value)){ targets.push({label: name, value: el.value}); } @@ -179,7 +183,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { */ function handleModalListChange(e) { const targets = []; - document.querySelectorAll('.chbox-mdl input').forEach(el => { + document.querySelector('#' + facet_item_id_for_search).querySelectorAll('.chbox-mdl input').forEach(el => { if(el.checked){ targets.push({label: name, value: el.value}); } @@ -228,7 +232,7 @@ function RangeCheckboxList({ values, name, labels, displayNumber }) { let dp = displayNumber == null ? 5 : displayNumber; return ( -
+
{values.length > dp &&