diff --git a/composer.json b/composer.json index 4be83db..4eeb8ee 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ ], "require": { "php": ">=7.1", + "10up/elasticpress": "~4.2.2", "humanmade/debug-bar-elasticpress": "~1.6.3" }, "autoload" : { @@ -23,6 +24,7 @@ "extra": { "altis": { "install-overrides": [ + "10up/elasticpress", "humanmade/debug-bar-elasticpress" ] } diff --git a/inc/namespace.php b/inc/namespace.php index e969d80..47c5d6e 100644 --- a/inc/namespace.php +++ b/inc/namespace.php @@ -164,7 +164,7 @@ function load_elasticpress() { add_filter( 'pre_site_option_ep_last_sync', '__return_false' ); } - require_once dirname( __DIR__ ) . '/lib/elasticpress/elasticpress.php'; + require_once Altis\ROOT_DIR . '/vendor/10up/elasticpress/elasticpress.php'; // Now ElasticPress has been included, we can remove some of it's filters. diff --git a/lib/elasticpress/assets/css/autosuggest.css b/lib/elasticpress/assets/css/autosuggest.css deleted file mode 100644 index 6361838..0000000 --- a/lib/elasticpress/assets/css/autosuggest.css +++ /dev/null @@ -1,41 +0,0 @@ -.ep-autosuggest-container { - position: relative; - - & .ep-autosuggest { - background: #fff; - border: 1px solid #ccc; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - display: none; - position: absolute; - - width: 100%; - z-index: 200; - - & > ul { - list-style: none; - margin: 0 !important; - - & > li { - font-family: sans-serif; - - & > a.autosuggest-link { - color: #000; - cursor: pointer; - display: block; - padding: 2px 10px; - - &:hover, - &:active { - background-color: #eee; - text-decoration: none; - } - } - } - } - } - - & .selected { - background-color: #eee; - text-decoration: none; - } -} diff --git a/lib/elasticpress/assets/css/comments.css b/lib/elasticpress/assets/css/comments.css deleted file mode 100644 index f0fdce6..0000000 --- a/lib/elasticpress/assets/css/comments.css +++ /dev/null @@ -1,13 +0,0 @@ -.ep-widget-search-comments-results { - list-style-type: none; - margin-left: 0; -} - -.ep-widget-search-comments-result-item, -.ep-widget-search-comments-result-item-not-found { - margin-left: 0; -} - -.ep-widget-search-comments-result-item.selected a { - border: 2px dotted #171923; -} diff --git a/lib/elasticpress/assets/css/dashboard.css b/lib/elasticpress/assets/css/dashboard.css deleted file mode 100644 index 7ed03b2..0000000 --- a/lib/elasticpress/assets/css/dashboard.css +++ /dev/null @@ -1,1186 +0,0 @@ -@import "@10up/component-tooltip"; - -:root { - --statusOk: #6aa000; - --statusWarning: #e3e600; - --statusError: #f00; -} - -html.wp-toolbar { - background: transparent; -} - -@custom-media --tablet-min (min-width: 880px); -@custom-media --wide-min (min-width: 1270px); -@custom-media --tablet-max (max-width: 880px); -@custom-media --wide-max (max-width: 1270px); - -/** - * Layout - */ - -.wrap .notice, -.wrap > h2 { - position: relative; - z-index: 2; -} - -.wrap h1 { - font-size: 23px; - font-weight: 400; - line-height: 29px; - margin: 0; - padding: 9px 0 4px; -} - -.wrap h2 { - color: #888; - line-height: 1.75; - margin: 0.5em 0 0.75em; -} - -#wpbody #update-nag, -#wpbody .update-nag { - margin-bottom: 20px; - margin-left: -20px; - margin-top: 0; - width: 100%; -} - -h2.ep-list-features { - display: none; -} - -/** - * Header - */ - -.ep-header-menu { - background-color: #fff; - border-bottom: 2px solid #ddd; - margin-left: -20px; - padding-bottom: 5px; - padding-left: 20px; - padding-right: 20px; - padding-top: 5px; - position: relative; - z-index: 2; -} - -.ep-header-menu::after { - clear: both; - content: " "; - display: block; -} - -.ep-header-menu img { - float: left; -} - -.ep-header-menu .icons { - display: inline-block; - float: right; - height: 39px; - line-height: 39px; - padding-right: 3px; -} - -.ep-header-menu .icons a { - color: inherit; - cursor: pointer; - font-size: 27px; - margin-left: 8px; - position: relative; - top: -3px; - vertical-align: middle; -} - -.ep-header-menu .icons .dashicons-no, -.ep-header-menu .icons .dashicons-controls-play, -.ep-header-menu .icons .dashicons-controls-pause, -.ep-header-menu .icons .dashicons-update, -.ep-header-menu .icons .dashicons-admin-generic { - font-size: 30px; - top: 1px; -} - -.ep-header-menu .resume-sync, -.ep-header-menu .pause-sync, -.ep-header-menu .cancel-sync { - display: none; -} - -.ep-header-menu .sync-status { - bottom: -1px; - color: #666; - display: none; - font-style: italic; - margin-right: 8px; - position: relative; - vertical-align: middle; -} - -.ep-header-menu .progress-bar { - background-color: #d84440; - bottom: 0; - height: 5px; - left: 0; - margin-bottom: -5px; - position: absolute; -} - -.ep-header-menu .icons .dashicons-update, -.ep-header-menu .icons .dashicons-admin-generic { - display: inline; -} - -/** - * Features - */ - -.ep-features { - overflow: auto; -} - -.error-overlay.cant-connect, -.error-overlay.syncing { - background-color: #fff; - bottom: 0; - content: " "; - display: block; - left: 0; - opacity: 0.6; - position: fixed; - right: 0; - top: 46px; - z-index: 1; -} - -@media (--tablet-min) { - - .error-overlay.cant-connect, - .error-overlay.syncing, - .error-overlay { - left: 160px; - top: 32px; - } -} - -.ep-feature .postbox { - margin-bottom: 0; -} - -.ep-feature .postbox .hndle { - cursor: inherit; -} - -.ep-feature .postbox .hndle .settings-button { - background-color: #efefef; - border: none; - border-radius: 4px; - color: inherit; - cursor: pointer; - display: inline-block; - float: right; - font-size: 13px; - font-weight: 400; - line-height: inherit; - margin-top: -4px; - padding: 4px 7px; - padding-left: 23px; - position: relative; -} - -.ep-feature .settings-button::before { - color: #72777c; - content: "\f140"; - display: inline-block; - font: 400 19px/1 dashicons; - left: 1px; - padding: 0 5px 0 0; - position: absolute; - top: 4px; - vertical-align: middle; -} - -.ep-feature .settings-button::after { - border-radius: 50%; - content: " "; - display: inline-block; - height: 8px; - margin-left: 10px; - margin-top: -2px; - vertical-align: middle; - width: 8px; -} - -.feature-requirements-status-2.ep-feature { - - & .postbox .hndle .settings-button::after { - background-color: transparent; - border: 1px solid var(--statusError); - } - - & .settings .requirements-status-notice { - border-color: var(--statusError); - } -} - -.feature-requirements-status-1.ep-feature { - - & .postbox .hndle .settings-button::after { - background-color: transparent; - border: 1px solid var(--statusWarning); - } - - &.feature-active .postbox .hndle .settings-button::after { - background-color: var(--statusWarning); - } - - & .settings .requirements-status-notice { - border-color: var(--statusWarning); - } -} - -.feature-requirements-status-0.ep-feature { - - & .postbox .hndle .settings-button::after { - background-color: transparent; - border: 1px solid var(--statusOk); - } - - &.feature-active .postbox .hndle .settings-button::after { - background-color: var(--statusOk); - } - - & .settings .requirements-status-notice { - border-color: var(--statusOk); - } -} - -.ep-feature { - margin-bottom: 20px; - position: relative; - vertical-align: top; -} - -.ep-feature.saving .action-wrap::before { - animation: load8 1.1s infinite linear; - border-bottom: 5px solid #ccc; - border-left: 5px solid #999; - border-radius: 50%; - border-right: 5px solid #ccc; - border-top: 5px solid #ccc; - content: " "; - display: inline-block; - font-size: 7px; - height: 8px; - margin-right: 1.4em; - position: relative; - text-indent: -9999em; - top: 4px; - transform: translateZ(0); - vertical-align: middle; - width: 8px; -} - -.ep-feature .description, -.ep-feature .settings { - margin-bottom: 0; - text-align: left; -} - -.ep-feature .settings { - display: none; - overflow: auto; - - & h3 { - font-size: inherit; - font-weight: 700; - margin-top: 0; - } -} - -.ep-feature.show-settings { - - & .settings { - display: block; - } - - & .description { - display: none; - } - - & .settings-button::before { - content: "\f142"; - } -} - -.ep-feature .settings .requirements-status-notice { - background-color: #efefef; - border-left: 4px solid var(--statusOk); - margin-bottom: 10px; - padding: 8px 12px; - - &.requirements-status-notice--reindex { - border-color: var(--statusWarning); - display: none; /* Controlled by JavaScript. */ - } -} - -.ep-feature .settings .action-wrap { - clear: both; - padding-top: 15px; - text-align: right; - vertical-align: middle; - - & a { - cursor: pointer; - display: inline-block; - } - - & .no-dash-sync { - color: #aaa; - display: none; - line-height: 27px; - padding-right: 8px; - } - - & a.disabled { - cursor: default; - } - - & .cancel { - margin-right: 6px; - margin-top: 4px; - } -} - -.ep-feature.dash-sync-disabled .settings .action-wrap { - - & .no-dash-sync { - display: inline; - } -} - -.ep-feature .settings .field { - clear: both; - overflow: initial; - padding-top: 15px; - - &:first-child { - padding-top: 0; - } - - & label { - display: inline-block; - margin: 0.25em 0 0.5em; - } - - & div { - padding-top: 5px; - } - - & > label, - & .field-name { - box-sizing: border-box; - display: block; - float: left; - padding-right: 1.5em; - width: 25%; - } - - & .input-wrap { - display: block; - float: right; - width: 75%; - } - - & .field-description { - color: #666; - font-style: italic; - margin-bottom: 0; - margin-top: 4px; - } - - & .disabled { - color: #bbb; - - & input { - cursor: default; - } - } - - & .components-form-token-field { - padding-top: 0; - } - - & .components-form-token-field__label { - margin-top: 0; - } - - & .components-form-token-field__input-container { - padding: 2px 4px; - } -} - -.ep-feature .long { - display: none; - - & p:last-child { - margin-bottom: 0; - } -} - -.ep-feature.show-full .long { - display: block; -} - -.ep-feature.show-full .learn-more { - display: none; -} - -.ep-feature .learn-more::after { - content: "\f140"; - font-family: dashicons; - font-size: 1.5em; - line-height: 1; - position: relative; - top: -0.07em; - vertical-align: middle; -} - -.ep-feature .collapse::after { - content: "\f142"; - font-family: dashicons; - font-size: 1.5em; - line-height: 1; - position: relative; - top: -0.07em; - vertical-align: middle; -} - -/** - * Intro Page - */ - - -.intro h2 { - line-height: 1.2; - padding: 9px 0 4px 0; -} - -.intro h1 { - color: #626262; - font-weight: 600; - margin: 0.5em 0 2em; - text-align: center; -} - -.intro-container { - display: flex; - flex-direction: column; - justify-content: center; - position: relative; - text-align: center; - - @media (--tablet-min) { - flex-direction: row; - } - - @media (--wide-min) { - margin-left: 10%; - margin-right: 10%; - } - - & h2 { - color: #626262; - height: 8%; - } - - & p { - margin-left: auto; - margin-right: auto; - max-width: 300px; - } -} - -.ep-feature-list { - display: inline-block; - margin: 0; - text-align: left; - vertical-align: top; - - & li { - align-items: center; - display: flex; - margin: 4px 3px; - } - - & [role="tooltip"].a11y-tip__help--top { - font-size: inherit; - } -} - -.ep-feature-info { - text-decoration: none; - - & .dashicons { - font-size: 14px; - position: relative; - top: 2px; - } -} - -.intro-box { - flex-grow: 2; - margin-bottom: 2em; -} - -.intro-container-success { - display: flex; - flex-wrap: wrap; - justify-content: center; - text-align: center; -} - -.intro-container-success p { - margin-top: 23px; - padding: 0 35% 0 35%; -} - -@media (--tablet-max) { - - .intro-container-success p { - margin-top: 23px; - padding: 0 26%; - } -} - -.ep-config-success span { - font-size: 6em; - padding-right: 75px; -} - -.setup-complete { - font-size: 2em; - margin-top: 0; - padding-top: 0; - width: 100%; -} - -.setup-complete h2 { - margin-top: 0; - padding-top: 0; -} - -.ep-copy-text { - - @media (--tablet-min) { - padding: 0 9% 0 9%; - } - - & a { - font-weight: 700; - } -} - -.ep-circle { - background: #fff; - border-radius: 50%; - color: #626262; - display: inline-block; - font-size: 16px; - font-weight: 400; - height: 6em; - line-height: 2.8em; - margin: 0 1em; - text-align: center; - width: 6em; - - & p { - border: 0; - font-size: 50px; - font-weight: 700; - line-height: 0.5em; - margin: 0; - } -} - -.ep-circle--first { - - @media (--tablet-min) { - - &::after { - background: #d4d4d4; - content: ""; - height: 0.2em; - left: 50%; - position: absolute; - top: 2.9em; - transform: translateX(-50%); - width: 75%; - z-index: -1; - } - } -} - -.ep-circle--active { - background-color: #d73c38; - color: #fff; -} - -@media (--tablet-min) { - - .wrap h1 { - line-height: 1.75; - } -} - -.wrap.intro { - margin-bottom: 30px; - margin-top: 30px; - overflow: auto; - - & .updated, - & .notice, - & .error, - & .is-dismissible { - display: none !important; - } -} - -.features-screenshot { - display: none; -} - -.setup-message { - clear: both; - text-align: center; -} - -.setup-button { - -webkit-appearance: none; - background-color: #e63e3b; - border: none; - border-radius: 10px; - box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.25); - color: #fff; - display: inline-block; - margin: 0 0.75em; - padding: 1em 1.5em; - text-decoration: none; -} - -.setup-button:hover { - background-color: #b93431; - color: #ffe8ed; -} - -.setup-message .setup-button-primary { - background-color: #fff; - color: #d84440; -} - -/** - * Weighting - */ - -.weighting-settings .postbox { - box-sizing: border-box; - max-width: 650px; - - & * { - box-sizing: border-box; - } -} - -.weighting-settings .postbox h2.hndle { - color: #444; - cursor: inherit; -} - -.weighting-settings fieldset { - padding: 10px; -} - -.weighting-settings fieldset legend { - float: left; /* legend cannot use display */ - position: relative; - top: 5px; - width: 100px; -} - -.weighting-settings fieldset p { - display: inline-block; - float: left; - margin: 0; -} - -.weighting-settings .field-group { - margin: 10px 0 0; -} - -.weighting-settings .field-group h3 { - font-size: 1em; - margin: 10px; -} - -.weighting-settings .fields > fieldset:nth-of-type(odd) { - background: #f9f9f9; -} - -.weighting-settings .searchable { - display: inline-block; - width: 120px; -} - -.weighting-settings .weighting { - align-items: center; - display: flex; -} - -.weighting-settings .weighting label { - margin-right: 10px; - min-width: 80px; -} - -.weighting-settings input[type="range"] { - -webkit-appearance: none; - background: transparent; - display: inline-block; - height: 1em; - margin: 0; - vertical-align: middle; - width: 200px; -} - -.weighting-settings input[type="range"]:focus { - outline: none; -} - -.weighting-settings input[type="range"]:disabled { - opacity: 0.5; - pointer-events: none; -} - -.weighting-settings input[type="range"]::-webkit-slider-runnable-track { - background: #ddd; - border: 0 solid #000; - border-radius: 1px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 3px; - width: 100%; -} - -.weighting-settings input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none; - background: #1e8cbe; - border: 1px solid #1e8cbe; - border-radius: 25px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 14px; - margin-top: -6px; - width: 14px; -} - -.weighting-settings input[type="range"]:focus::-webkit-slider-runnable-track { - background: #ddd; -} - -.weighting-settings input[type="range"]:focus::-webkit-slider-thumb { - background: #fff !important; -} - -.weighting-settings input[type="range"]::-moz-range-track { - background: #1e8cbe; - border: 0 solid #000; - border-radius: 1px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 3px; - width: 100%; -} - -.weighting-settings input[type="range"]::-moz-range-thumb { - background: #1e8cbe; - border: 1px solid #1e8cbe; - border-radius: 25px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 14px; - width: 14px; -} - -.weighting-settings input[type="range"]::-ms-track { - background: transparent; - border-color: transparent; - color: transparent; - cursor: pointer; - height: 3px; - width: 100%; -} - -.weighting-settings input[type="range"]::-ms-fill-lower { - background: #1e8cbe; - border: 0 solid #000; - border-radius: 2px; - box-shadow: 0 0 0 #000; -} - -.weighting-settings input[type="range"]::-ms-fill-upper { - background: #1e8cbe; - border: 0 solid #000; - border-radius: 2px; - box-shadow: 0 0 0 #000; -} - -.weighting-settings input[type="range"]::-ms-thumb { - background: #a1d0ff; - border: 1px solid #1e8cbe; - border-radius: 25px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 14px; - margin-top: 1px; - width: 14px; -} - -.weighting-settings input[type="range"]:focus::-ms-fill-lower { - background: #1e8cbe; -} - -.weighting-settings input[type="range"]:focus::-ms-fill-upper { - background: #1e8cbe; -} - -.ep-feature-search .wp-color-result.button { - margin-bottom: 10px; -} - - -.ep-feature.ep-feature-search .settings .wp-picker-input-wrap > label { - margin-right: 10px; - margin-top: 0; -} - -.ep-feature.ep-feature-search .settings.wp-picker-input-wrap label { - align-items: center; - display: flex; -} - -.ep-feature.ep-feature-search .settings .wp-picker-open + .wp-picker-input-wrap { - display: flex; -} - -/** - * Animations - */ - -@-webkit-keyframes load8 { - - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - -@keyframes load8 { - - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - -@media (--tablet-min) { - - .intro .left { - float: left; - width: 30%; - } - - .features-screenshot { - display: block; - float: right; - height: auto; - margin: 0 auto; - max-width: 1000px; - width: 70%; - } - - .ep-features .left { - box-sizing: border-box; - display: block; - float: left; - padding-right: 10px; - width: 50%; - } - - .ep-features .right { - box-sizing: border-box; - display: block; - float: right; - padding-left: 10px; - width: 50%; - } - - .ep-feature .feature-message { - display: inline-block; - float: left; - padding: 0; - padding-top: 0.5em; - vertical-align: middle; - } -} - -/** - * Settings page - */ - -/* @media screen and (max-width: 600px) { */ -.wrap h2.nav-tab-wrapper.ep-credentials-tabs { - border-bottom: 1px solid #ccc; -} - -/* } */ - -.ep-credentials input[type="text"] { - min-width: 250px; -} - -h2 .nav-tab.ep-credentials-tab { - cursor: pointer; - margin-bottom: -1px; -} - -.ep-credentials-tab.nav-tab-active { - border-bottom-color: #f1f1f1; -} - -.ep-credentials-tab img, -.ep-credentials-tab span { - display: inline-block; - vertical-align: middle; -} - -.ep-credentials fieldset { - border-bottom: 1px solid #ccc; - border-left: 1px solid #ccc; - border-right: 1px solid #ccc; - margin: 0; - padding: 0 1rem; -} - -.ep-credentials fieldset.predefined { - border-top: 1px solid #ccc; -} - -.ep-settings .description { - font-size: 0.75rem; - font-style: italic; -} - -@media (--tablet-min) { - - .ep-credentials { - display: inline-block; - } - - .ep-credentials .form-table { - width: auto; - } - - .ep-credentials .form-table td { - padding-left: 0; - } -} - -.ep-flex-container { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - width: 100%; -} - -.ep-flex-container-nowrap { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - width: 100%; -} - -.stats-list { - margin-right: 0; - width: 100%; - - /* @media( --tablet-min ) { - width: 48%; - margin-right: 24px; - } */ - - @media (--wide-min) { - background-color: #fff; - margin-right: 28px; - width: 37%; - } -} - -.stats-queries { - background-color: #fff; - width: 100%; - - /* @media( --tablet-min ) { - width: 48%; - } */ - - @media (--wide-min) { - width: 60%; - } -} - -.stats-list-th span:last-of-type { - border-radius: 4px; - color: inherit; - cursor: pointer; - display: inline-block; - float: right; - font-size: 13px; - margin-top: -4px; - padding: 4px 7px; - padding-left: 23px; - position: relative; -} - -.status-circle { - float: right; - text-transform: capitalize; -} - -.status-circle::after { - border-radius: 50%; - content: " "; - display: inline-block; - height: 8px; - margin-left: 10px; - margin-top: -2px; - vertical-align: middle; - width: 8px; -} - -.green-status { - color: var(--statusOk); -} - -.yellow-status { - color: var(--statusWarning); -} - -.red-status { - color: var(--statusError); -} - -.green-status::after { - background-color: var(--statusOk); -} - -.yellow-status::after { - background-color: var(--statusWarning); -} - -.red-status::after { - background-color: var(--statusError); -} - -.doc-chart { - margin-right: 0; - width: 100%; - - @media (--tablet-min) { - margin-right: 24px; - width: 48%; - } - - @media (--wide-min) { - margin-right: 28px; - width: 50%; - } -} - -.ep-qchart-container { - margin: 0 auto; - width: 90%; - - @media (--tablet-min) { - width: 48%; - } - - @media (--wide-min) { - margin: 0 auto; - width: 50%; - } - -} - -.inside-totals { - padding: 0 12px 12px; -} - -.ep-totals { - display: flex; - flex-direction: column; - width: 100%; - - @media (--tablet-min) { - width: 48%; - } - - @media (--wide-min) { - width: 47%; - } - - & .ep-flex-container { - flex-direction: column; - justify-content: center; - margin-bottom: auto; - margin-top: auto; - - @media (--wide-min) { - flex-direction: row; - } - } -} - -.ep-totals-column { - box-sizing: border-box; - text-align: center; - width: 100%; - - @media (--wide-min) { - width: 50%; - } -} - -p.ep-totals-title { - font-weight: bolder; - margin-top: 28px; -} - -p.ep-totals-data { - font-size: 3em; - margin-top: 22px; -} diff --git a/lib/elasticpress/assets/css/facets-admin.css b/lib/elasticpress/assets/css/facets-admin.css deleted file mode 100644 index 5a58aa0..0000000 --- a/lib/elasticpress/assets/css/facets-admin.css +++ /dev/null @@ -1,3 +0,0 @@ -.widget-ep-facet label { - margin-right: 5px; -} diff --git a/lib/elasticpress/assets/css/facets.css b/lib/elasticpress/assets/css/facets.css deleted file mode 100644 index 59451e1..0000000 --- a/lib/elasticpress/assets/css/facets.css +++ /dev/null @@ -1,93 +0,0 @@ -.widget_ep-facet input[type="search"] { - margin-bottom: 1rem; -} - -.widget_ep-facet .searchable .inner { - max-height: 20em; - overflow: scroll; -} - -.widget_ep-facet .term.hide { - display: none; -} - -.widget_ep-facet .empty-term { - opacity: 0.5; - position: relative; -} - -.widget_ep-facet .empty-term::after { - bottom: 0; - content: " "; - display: block; - left: 0; - position: absolute; - right: 0; - top: 0; - width: 100%; - z-index: 2; -} - -.widget_ep-facet .level-1 { - padding-left: 20px; -} - -.widget_ep-facet .level-2 { - padding-left: 40px; -} - -.widget_ep-facet .level-3 { - padding-left: 60px; -} - -.widget_ep-facet .level-4 { - padding-left: 80px; -} - -.widget_ep-facet .level-5 { - padding-left: 100px; -} - -.widget_ep-facet input[disabled] { - cursor: pointer; - opacity: 1; -} - -.widget_ep-facet .term a { - align-items: center; - display: flex; - position: relative; -} - -.ep-checkbox { - align-items: center; - background-color: #eee; - display: flex; - flex-shrink: 0; - height: 1em; - justify-content: center; - margin-right: 0.25em; - width: 1em; -} - -.ep-checkbox::after { - border: solid #fff; - border-width: 0 0.125em 0.125em 0; - content: ""; - display: none; - height: 0.5em; - transform: rotate(45deg); - width: 0.25em; -} - -.ep-checkbox.checked { - background-color: #5e5e5e; -} - -.ep-checkbox.checked::after { - display: block; -} - -.widget_ep-facet .term a:hover .ep-checkbox { - background-color: #ccc; -} diff --git a/lib/elasticpress/assets/css/highlighting.css b/lib/elasticpress/assets/css/highlighting.css deleted file mode 100644 index 96589c5..0000000 --- a/lib/elasticpress/assets/css/highlighting.css +++ /dev/null @@ -1,5 +0,0 @@ -.ep-highlight { - background-color: transparent; - font-style: italic; - font-weight: 700; -} diff --git a/lib/elasticpress/assets/css/instant-results.css b/lib/elasticpress/assets/css/instant-results.css deleted file mode 100644 index ba17c2b..0000000 --- a/lib/elasticpress/assets/css/instant-results.css +++ /dev/null @@ -1,28 +0,0 @@ -@import "instant-results/utilities.css"; -@import "instant-results/checkbox.css"; -@import "instant-results/input.css"; -@import "instant-results/modal.css"; -@import "instant-results/options-list.css"; -@import "instant-results/page.css"; -@import "instant-results/panel.css"; -@import "instant-results/pagination.css"; -@import "instant-results/range-slider.css"; -@import "instant-results/result.css"; -@import "instant-results/results.css"; -@import "instant-results/sidebar.css"; -@import "instant-results/sidebar-toggle.css"; -@import "instant-results/sort.css"; -@import "instant-results/toolbar.css"; - -:root { - --ep-search-background-color: #fff; - --ep-search-alternate-background-color: #efefef; - --ep-search-border-color: #dfdfdf; - --ep-search-range-thumb-size: 1.625em; - --ep-search-range-track-size: 0.75em; - - @media ( min-width: 768px ) { - --ep-search-range-thumb-size: 1.25em; - --ep-search-range-track-size: 0.5em; - } -} diff --git a/lib/elasticpress/assets/css/instant-results/checkbox.css b/lib/elasticpress/assets/css/instant-results/checkbox.css deleted file mode 100644 index 1cc9ab4..0000000 --- a/lib/elasticpress/assets/css/instant-results/checkbox.css +++ /dev/null @@ -1,7 +0,0 @@ -.ep-search-checkbox__count::before { - content: "("; -} - -.ep-search-checkbox__count::after { - content: ")"; -} diff --git a/lib/elasticpress/assets/css/instant-results/input.css b/lib/elasticpress/assets/css/instant-results/input.css deleted file mode 100644 index 1b527ed..0000000 --- a/lib/elasticpress/assets/css/instant-results/input.css +++ /dev/null @@ -1,5 +0,0 @@ -.ep-search-input { - font-size: 1.25em; - margin: 0 !important; - width: 100%; -} diff --git a/lib/elasticpress/assets/css/instant-results/modal.css b/lib/elasticpress/assets/css/instant-results/modal.css deleted file mode 100644 index 081dfff..0000000 --- a/lib/elasticpress/assets/css/instant-results/modal.css +++ /dev/null @@ -1,60 +0,0 @@ -.has-ep-search-modal { - overflow: hidden; -} - -.ep-search-modal { - --ep-search-modal-focus-within: 0; - background-color: rgba(43, 46, 56, 0.9); - bottom: 0; - display: flex; - left: 0; - position: fixed; - right: 0; - top: 0; - z-index: 9999; - - @nest .rtl & { - direction: rtl; - text-align: right; - } - - @nest .admin-bar & { - top: 32px; - - @media ( max-width: 782px ) { - top: 46px; - } - } - - &[aria-hidden="true"] { - display: none; - } - - &:focus-within { - --ep-search-modal-focus-within: 1; - } -} - -.ep-search-modal__content { - background-color: var(--ep-search-background-color); - bottom: 0; - display: flex; - flex-direction: column; - left: 0; - position: absolute; - right: 0; - top: 0; - - @media ( min-width: 768px ) { - bottom: 1em; - margin: 0 auto; - max-width: calc(100% - 2em); - top: 1em; - width: 80em; - } -} - -.ep-search-modal__close { - align-self: flex-end; - padding: 1em !important; -} diff --git a/lib/elasticpress/assets/css/instant-results/options-list.css b/lib/elasticpress/assets/css/instant-results/options-list.css deleted file mode 100644 index 0624818..0000000 --- a/lib/elasticpress/assets/css/instant-results/options-list.css +++ /dev/null @@ -1,17 +0,0 @@ -.ep-search-options-list { - list-style: none; - margin: 0; - padding: 0; -} - -.ep-search-options-list__item { - margin: 0.5em 0; - - &::before { - content: none; - } -} - -.ep-search-options-list__sub-menu { - padding-left: 1em; -} diff --git a/lib/elasticpress/assets/css/instant-results/page.css b/lib/elasticpress/assets/css/instant-results/page.css deleted file mode 100644 index 5fba4e1..0000000 --- a/lib/elasticpress/assets/css/instant-results/page.css +++ /dev/null @@ -1,39 +0,0 @@ -.ep-search-page { - display: flex; - flex-direction: column; - flex-grow: 2; - margin: 0; - overflow-y: auto; - transition: opacity 300ms ease-out; - width: 100%; - - @media ( min-width: 768px ) { - overflow: hidden; - } - - & *, - & *::before, - & *::after { - box-sizing: border-box; - } - - &.is-loading { - opacity: 0.5; - } -} - -.ep-search-page__header, -.ep-search-page__tools, -.ep-search-page__body { - padding: 0 1em; -} - -.ep-search-page__body { - - @media ( min-width: 768px ) { - align-items: flex-start; - display: flex; - flex-grow: 2; - overflow: hidden; - } -} diff --git a/lib/elasticpress/assets/css/instant-results/pagination.css b/lib/elasticpress/assets/css/instant-results/pagination.css deleted file mode 100644 index c0eb1b2..0000000 --- a/lib/elasticpress/assets/css/instant-results/pagination.css +++ /dev/null @@ -1,19 +0,0 @@ -.ep-search-pagination { - align-items: center; - display: grid; - grid-template-columns: 1fr 1fr 1fr; - margin-top: auto; - text-align: center; - - @nest .rtl & { - direction: rtl; - } -} - -.ep-search-pagination__next { - justify-self: end; -} - -.ep-search-pagination__previous { - justify-self: start; -} diff --git a/lib/elasticpress/assets/css/instant-results/panel.css b/lib/elasticpress/assets/css/instant-results/panel.css deleted file mode 100644 index cfa0620..0000000 --- a/lib/elasticpress/assets/css/instant-results/panel.css +++ /dev/null @@ -1,27 +0,0 @@ -.ep-search-panel { - border: 1px solid var(--ep-search-border-color); - margin: 0; - padding: 0; - - @nest .ep-search-panel + & { - border-top-width: 0; - } -} - -.ep-search-panel__heading { - font-size: inherit; - margin: 0; -} - -.ep-search-panel__button { - padding: 1em !important; - width: 100% !important; -} - -.ep-search-panel__content { - padding: 0 1em 1em 1em; - - &[aria-hidden="true"] { - display: none; - } -} diff --git a/lib/elasticpress/assets/css/instant-results/range-slider.css b/lib/elasticpress/assets/css/instant-results/range-slider.css deleted file mode 100644 index c224807..0000000 --- a/lib/elasticpress/assets/css/instant-results/range-slider.css +++ /dev/null @@ -1,26 +0,0 @@ -.ep-search-range-slider { - align-items: center; - display: flex; - margin: 0.5em 0; - min-height: var(--ep-search-range-thumb-size); -} - -.ep-search-range-slider__track { - background: var(--ep-search-alternate-background-color); - border-radius: calc(var(--ep-search-range-track-size) / 2); - height: var(--ep-search-range-track-size); -} - -.ep-search-range-slider__track-1 { - background-color: currentColor; -} - -.ep-search-range-slider__thumb { - background-color: currentColor; - border-radius: calc(var(--ep-search-range-thumb-size) / 2); - box-shadow: - inset 0 0 0 calc(var(--ep-search-range-thumb-size) / 10) currentColor, - inset 0 0 0 calc((var(--ep-search-range-thumb-size) - var(--ep-search-range-track-size)) / 2) var(--ep-search-background-color); - height: var(--ep-search-range-thumb-size); - width: var(--ep-search-range-thumb-size); -} diff --git a/lib/elasticpress/assets/css/instant-results/result.css b/lib/elasticpress/assets/css/instant-results/result.css deleted file mode 100644 index cce353b..0000000 --- a/lib/elasticpress/assets/css/instant-results/result.css +++ /dev/null @@ -1,89 +0,0 @@ -.ep-search-result { - align-items: flex-start; - display: grid; - grid-gap: 0.5em; - grid-template-areas: - "header" - "footer"; - grid-template-rows: auto 1fr; - - @media ( min-width: 768px ) { - grid-gap: 1em; - grid-template-areas: - "header" - "description" - "footer"; - grid-template-rows: auto auto 1fr; - } -} - -.ep-search-result--has-thumbnail { - grid-template-areas: - "thumbnail header" - "thumbnail footer"; - grid-template-columns: min(300px, 34%) auto; - - @media ( min-width: 768px ) { - grid-template-areas: - "thumbnail header" - "thumbnail description" - "thumbnail footer"; - } -} - -.ep-search-result__thumbnail { - display: block; - grid-area: thumbnail; - - & img { - display: block; - margin: 0; - width: 100%; - } -} - -.ep-search-result__header { - display: grid; - grid-area: header; - grid-gap: 0.5em; - grid-template-columns: auto; - justify-items: start; -} - -.ep-search-result__title { - font-size: 1em; - margin: 0; - - @media ( min-width: 768px ) { - font-size: 1.25em; - } -} - -.ep-search-result__type { - background-color: var(--ep-search-alternate-background-color); - border-radius: 0.25em; - display: inline-block; - font-size: 0.875em; - line-height: 1.5; - padding: 0 0.25em; - vertical-align: text-bottom; -} - -.ep-search-result__description { - display: none; - font-size: 0.875em; - grid-area: description; - margin: 0; - - @media ( min-width: 768px ) { - display: block; - font-size: 1em; - } -} - -.ep-search-result__footer { - display: grid; - grid-area: footer; - grid-gap: 0.5em; - justify-items: start; -} diff --git a/lib/elasticpress/assets/css/instant-results/results.css b/lib/elasticpress/assets/css/instant-results/results.css deleted file mode 100644 index eb3dc3c..0000000 --- a/lib/elasticpress/assets/css/instant-results/results.css +++ /dev/null @@ -1,30 +0,0 @@ -.ep-search-results { - display: grid; - grid-gap: 2em; - grid-template-columns: 100%; - grid-template-rows: max-content; - padding: 0 0 1em 0; - width: 100%; - - @media ( min-width: 768px ) { - height: 100%; - overflow-y: auto; - padding: 0 1em 1em 1em; - } -} - -.ep-search-results__header { - align-items: center; - display: flex; - gap: 1em; - justify-content: space-between; -} - -.ep-search-results__title { - font-size: 1.25em; - margin: 0 !important; - - @media ( min-width: 768px ) { - font-size: 1.5em; - } -} diff --git a/lib/elasticpress/assets/css/instant-results/sidebar-toggle.css b/lib/elasticpress/assets/css/instant-results/sidebar-toggle.css deleted file mode 100644 index 10c3200..0000000 --- a/lib/elasticpress/assets/css/instant-results/sidebar-toggle.css +++ /dev/null @@ -1,7 +0,0 @@ -.ep-search-sidebar-toggle { - width: 100%; - - @media ( min-width: 768px ) { - display: none; - } -} diff --git a/lib/elasticpress/assets/css/instant-results/sidebar.css b/lib/elasticpress/assets/css/instant-results/sidebar.css deleted file mode 100644 index c3d2253..0000000 --- a/lib/elasticpress/assets/css/instant-results/sidebar.css +++ /dev/null @@ -1,15 +0,0 @@ -.ep-search-sidebar { - display: none; - margin-bottom: 2em; - - &.is-open { - display: block; - } - - @media ( min-width: 768px ) { - display: block; - max-height: calc(100% - 1em); - min-width: 25%; - overflow-y: auto; - } -} diff --git a/lib/elasticpress/assets/css/instant-results/sort.css b/lib/elasticpress/assets/css/instant-results/sort.css deleted file mode 100644 index be9e085..0000000 --- a/lib/elasticpress/assets/css/instant-results/sort.css +++ /dev/null @@ -1,24 +0,0 @@ -.ep-search-sort { - flex-shrink: 0; - gap: 0.5em; - margin: 0; - - @nest .ep-search-results & { - display: none; - - @media ( min-width: 768px ) { - align-items: center; - display: flex; - } - } - - @nest .ep-search-sidebar & { - display: flex; - flex-direction: column; - margin-bottom: 1em; - - @media ( min-width: 768px ) { - display: none; - } - } -} diff --git a/lib/elasticpress/assets/css/instant-results/toolbar.css b/lib/elasticpress/assets/css/instant-results/toolbar.css deleted file mode 100644 index ced39b1..0000000 --- a/lib/elasticpress/assets/css/instant-results/toolbar.css +++ /dev/null @@ -1,11 +0,0 @@ -.ep-search-toolbar { - align-items: start; - display: flex; - flex-wrap: wrap; - gap: 0.25em; - margin: 1em 0; - - @media ( min-width: 768px ) { - align-items: center; - } -} diff --git a/lib/elasticpress/assets/css/instant-results/utilities.css b/lib/elasticpress/assets/css/instant-results/utilities.css deleted file mode 100644 index 38d3674..0000000 --- a/lib/elasticpress/assets/css/instant-results/utilities.css +++ /dev/null @@ -1,47 +0,0 @@ -.ep-search-reset-button { - font: inherit !important; - height: auto !important; - letter-spacing: inherit !important; - line-height: 1 !important; - margin: 0 !important; - padding: 0 !important; - text-align: inherit !important; - text-transform: inherit !important; - width: auto !important; - - &, - &:focus, - &:hover { - background: transparent !important; - border: none !important; - box-shadow: none !important; - color: inherit !important; - cursor: default !important; - } - - &:focus { - outline: medium auto Highlight !important; - outline: medium auto -webkit-focus-ring-color !important; - outline-offset: 0 !important; - } -} - -.ep-search-small-button { - font-size: 0.875em !important; - height: auto !important; - line-height: 1 !important; - padding: 0.5em !important; -} - -.ep-search-icon-button { - align-items: center; - display: flex; - justify-content: space-between; - - & svg { - fill: currentColor; - flex-shrink: 0; - height: 1em; - width: 1em; - } -} diff --git a/lib/elasticpress/assets/css/ordering.css b/lib/elasticpress/assets/css/ordering.css deleted file mode 100644 index ef15981..0000000 --- a/lib/elasticpress/assets/css/ordering.css +++ /dev/null @@ -1,143 +0,0 @@ -#ep-ordering { - border-left-width: 0; - border-right-width: 0; - - & .inside { - background-color: #f1f1f1; - margin: 0; - padding: 0; - } - - & .pointers, - & .pointer-search, - & .loading { - background-color: #fff; - border-left: 1px solid #eee; - border-right: 1px solid #eee; - } - - & .new-post { - background: #fff; - padding: 0 1em; - } - - & .loading { - padding: 1em; - - & .spinner { - float: left; - margin-left: 0; - margin-top: 0; - } - } - - & .pointer-type { - border: 2px solid #0073aa; - border-radius: 2px; - color: #0073aa; - display: inline-block; - font-size: 0.75em; - font-weight: 700; - margin-right: 8px; - padding: 1px 2px; - } - - & .pointers { - - & .pointer, - & .post { - padding: 1em; - - &:nth-child(odd) { - background-color: #f9f9f9; - } - } - - & .title { - color: #0073aa; - } - - & .pointer-actions { - float: right; - - & .handle { - cursor: move; - } - - & .delete-pointer { - margin-left: 10px; - } - } - - & .next-page-notice { - background-color: #fdeeca; - padding: 1em 0; - text-align: center; - } - } - - & .legend { - background: #fff; - border-bottom: 1px solid #eee; - padding: 1em 0; - text-align: center; - } - - & .legend-item { - display: inline-block; - font-size: 0.875em; - font-style: italic; - margin: 0 0.5em; - } - - & .pointer-search { - margin-top: 2em; - - & .no-results { - padding: 1em; - } - - & .section-title { - border-bottom: 1px solid #eee; - border-top: 1px solid #eee; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); - font-weight: 700; - } - - & .search-wrapper { - padding: 1em 0; - } - - & .input-wrap { - padding: 0 1em; - } - - & .search-pointers { - font-size: 18px; - height: 1.7em; - line-height: 100%; - padding: 3px 8px; - } - - & .pointer-results { - padding: 1em 0 0; - } - - & .pointer-result { - padding: 10px 2em; - - & .dashicons { - float: right; - } - - &:hover { - background-color: #f9f9f9; - } - } - } - - & .delete-pointer, - & .add-pointer { - cursor: pointer; - } -} diff --git a/lib/elasticpress/assets/css/related-posts-block.css b/lib/elasticpress/assets/css/related-posts-block.css deleted file mode 100644 index 82babec..0000000 --- a/lib/elasticpress/assets/css/related-posts-block.css +++ /dev/null @@ -1,9 +0,0 @@ -.editor-styles-wrapper .wp-block-elasticpress-related-posts ul, -.wp-block-elasticpress-related-posts ul { - list-style-type: none; - padding: 0; -} - -.editor-styles-wrapper .wp-block-elasticpress-related-posts ul li a > div { - display: inline; -} diff --git a/lib/elasticpress/assets/css/sites-admin.css b/lib/elasticpress/assets/css/sites-admin.css deleted file mode 100644 index 8b987d1..0000000 --- a/lib/elasticpress/assets/css/sites-admin.css +++ /dev/null @@ -1,65 +0,0 @@ -/* Switch toggle on network admin sites page. */ -.switch { - display: inline-block; - height: 34px; - position: relative; - width: 60px; -} - -.switch input { - height: 0; - opacity: 0; - width: 0; -} - -.slider { - background-color: #ccc; - bottom: 0; - cursor: pointer; - left: 0; - position: absolute; - right: 0; - top: 0; - -webkit-transition: 0.4s; - transition: 0.4s; -} - -.slider::before { - background-color: #fff; - bottom: 4px; - content: ""; - height: 26px; - left: 4px; - position: absolute; - -webkit-transition: 0.4s; - transition: 0.4s; - width: 26px; -} - -input:checked + .slider { - background-color: #2196f3; -} - -input:focus + .slider { - box-shadow: 0 0 1px #2196f3; -} - -input:checked + .slider::before { - -webkit-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); -} - -/* Rounded sliders */ -.slider.round { - border-radius: 34px; -} - -.slider.round::before { - border-radius: 50%; -} - -.switch-label { - padding-left: 20px; - padding-top: 20px; -} diff --git a/lib/elasticpress/assets/css/sync.css b/lib/elasticpress/assets/css/sync.css deleted file mode 100644 index ad438b1..0000000 --- a/lib/elasticpress/assets/css/sync.css +++ /dev/null @@ -1,329 +0,0 @@ -:root { - --ep-admin-color-base-white: #fff; - --ep-admin-color-blue-01: #00a0d2; - --ep-admin-color-grey-01: #f1f1f1; - --ep-admin-color-green-01: #46b450; - --ep-admin-color-red-01: #b52727; - --ep-admin-color-dark-01: #333; - --ep-admin-max-width: 1200px; - - --ep-admin-box-title: #1d2327; - - --ep-admin-delete-sync-button-bg-color: rgba(181, 39, 39, 0.03); - - --ep-admin-progress-bar-bg-color: rgba(0, 160, 210, 0.3); - - --ep-admin-output-tab-color: #1e1e1e; - - --ep-admin-log-bg-color: #1a1e24; - --ep-admin-log-line-number-color: #999; - --ep-admin-log-line-number-bg-color: #303030; - -} - -.elasticpress_page_elasticpress-sync .button:disabled { - cursor: not-allowed; -} - -.ep-sync-box__progress-wrapper { - display: none; -} - -.ep-sync-box__output { - background-color: var(--ep-admin-log-bg-color); - display: none; - margin-bottom: 20px; - max-height: 200px; - overflow-y: scroll; - position: relative; -} - -.ep-sync-box__output_active { - display: block; -} - -.ep-sync-box__output-wrapper { - color: var(--ep-admin-color-base-white); - margin-left: 30px; - min-height: 200px; -} - -.ep-sync-box__output-line { - position: relative; -} - -.ep-sync-box__output-line-number { - background-color: var(--ep-admin-log-line-number-bg-color); - color: var(--ep-admin-log-line-number-color); - left: -30px; - min-width: 20px; - padding: 0 3px 0 5px; - position: absolute; - text-align: right; - white-space: nowrap; -} - -.ep-sync-box__output-line-text { - font-size: 12px; - padding-left: 14px; -} - -.ep-sync-box__output-tabs { - align-items: center; - display: flex; -} - -.ep-sync-box__output-tabs_hide { - display: none; -} - -.ep-sync-box__output-tab { - color: var(--ep-admin-output-tab-color); - padding: 16px; -} - -.ep-sync-box__output-tab:hover { - cursor: pointer; -} - -.ep-sync-box__output-tab_active { - border-bottom: 4px solid var(--ep-admin-color-blue-01); - padding-bottom: 12px; -} - -.ep-sync-box__output-tab_active:hover { - cursor: default; -} - -.ep-sync-box__button-text { - height: 21px; -} - -.elasticpress_page_elasticpress-sync .card { - max-width: var(--ep-admin-max-width); - - & .ep-sync-box__description-actions { - display: flex; - flex-direction: column; - - @media (min-width: 768px) { - flex-direction: row; - justify-content: space-between; - } - } - - & .ep-sync-box__description { - - @media (min-width: 768px) { - width: 69%; - } - } - - & .ep-sync-box__action { - align-items: center; - display: flex; - flex-direction: column; - margin-top: 40px; - - @media (min-width: 768px) { - width: 30%; - } - - & .ep-sync-box__button { - align-items: center; - display: flex; - font-size: 24px; - font-weight: 700; - height: 68px; - justify-content: space-between; - padding: 20px 40px; - width: 228px; - } - - & .ep-sync-box__icon-button { - font-size: 28px; - height: 28px; - width: 28px; - - } - - & .ep-sync-box__learn-more-link { - margin-top: 19px; - } - - } - - & .ep-sync-box__description_text { - font-size: 18px; - } - - & .ep-last-sync { - margin-bottom: 12px; - } - - & .ep-last-sync__title { - font-size: 20px; - font-weight: 700; - margin-bottom: 0.5em; - } - - & .ep-last-sync__date { - background-color: var(--ep-admin-color-grey-01); - padding: 6px; - } - - & .ep-sync-box__buttons { - display: flex; - - } - - & .ep-sync-box__button-resume, - & .ep-sync-box__button-pause, - & .ep-sync-box__button-stop { - align-items: center; - display: none; - flex-direction: column; - height: 68px; - justify-content: center; - width: 100px; - } - - & .ep-sync-box__button-stop { - background-color: var(--ep-admin-color-blue-01); - border-color: var(--ep-admin-color-blue-01); - margin-left: 24px; - } - - & .ep-sync-box__progress { - align-items: normal; - display: flex; - flex-direction: column; - margin-bottom: 5px; - margin-top: 19px; - - @media (min-width: 768px) { - align-items: center; - flex-direction: row; - } - } - - & .ep-sync-box__sync-in-progress { - display: flex; - flex-direction: row; - - @media (min-width: 768px) { - width: 30%; - } - } - - & .ep-sync-box__progressbar { - background-color: var(--ep-admin-color-grey-01); - border-radius: 24px; - height: 20px; - margin: 15px 0; - position: relative; - width: 100%; - - @media (min-width: 768px) { - margin: 0; - width: 65%; - } - } - - & .ep-sync-box__progressbar_animated { - background-color: var(--ep-admin-progress-bar-bg-color); - color: var(--ep-admin-color-dark-01); - display: block; - height: 100%; - margin: 0; - text-align: center; - transition: width 0.5s ease-in-out; - width: 0; - } - - & .ep-sync-box__progressbar_complete { - background-color: var(--ep-admin-color-green-01); - color: var(--ep-admin-color-base-white); - } - - & .ep-sync-box__sync-in-progress-info { - display: flex; - flex-direction: column; - margin-left: 12px; - } - - & .ep-sync-box__progress-info { - font-size: 14px; - font-weight: 500; - margin-bottom: 5px; - } - - & .ep-sync-box__start-time { - color: var(--ep-admin-color-blue-01); - font-size: 14px; - } - - & .ep-sync-box__start-time-date { - color: var(--ep-admin-color-dark-01); - } -} - -.elasticpress_page_elasticpress-sync .ep-delete-data-and-sync { - margin-top: 40px; - - & .card { - margin-top: 17px; - } - - & .ep-delete-data-and-sync__title { - color: var(--ep-admin-box-title); - font-size: 18px; - font-weight: 400; - } - - & .ep-delete-data-and-sync__warning { - align-items: flex-start; - display: flex; - - @media (min-width: 768px) { - width: 69%; - } - } - - & .ep-delete-data-and-sync__warning-icon { - margin-right: 9px; - margin-top: 17px; - } - - & .ep-delete-data-and-sync__button { - background-color: var(--ep-admin-delete-sync-button-bg-color); - border: 1px solid var(--ep-admin-color-red-01); - color: var(--ep-admin-color-red-01); - margin: 5px 0 12px; - } - - & .ep-delete-data-and-sync__button-cancel { - display: none; - } - - & .ep-sync-box__action { - flex-direction: column; - height: auto; - justify-content: center; - margin-top: 0; - width: 100%; - - @media (min-width: 768px) { - flex-direction: row; - height: 68px; - justify-content: space-between; - } - } - - & .ep-sync-box__buttons { - - @media (min-width: 768px) { - margin-right: 5%; - } - } -} diff --git a/lib/elasticpress/assets/css/synonyms.css b/lib/elasticpress/assets/css/synonyms.css deleted file mode 100644 index ac6f60f..0000000 --- a/lib/elasticpress/assets/css/synonyms.css +++ /dev/null @@ -1,97 +0,0 @@ -#synonym-root { - - & .page-title-action { - margin-left: 10px; - } - - & .postbox .hndle { - cursor: default; - } - - & h2 { - color: #23282d; - } -} - -.synonym-alternatives-editor, -.synonym-set-editor { - display: flex; - - & > .postbox { - width: 100%; - - & > .hndle { - display: flex; - } - } - - & .synonym-alternative-editor { - display: flex; - } - - & .ep-synonyms__linked-multi-input { - flex: 1; - margin-bottom: 0.5em; - } - - & .ep-synonyms__input { - border: 1px solid hsl(0, 0%, 80%); - margin-bottom: 0.5em; - margin-right: 1em; - width: 10em; - } - - & .synonym-alternatives__primary-heading { - width: 11em; - } - - & .synonym-alternatives__input-heading { - flex: 1; - } - - & .ep-synonyms__linked-multi-input input:focus { - box-shadow: none; - } - - &.invalid input { - border-color: #a00; - } -} - -.synonym-alternative-editor, -.synonym-set-editor { - margin-top: 0.625em; -} - -button.synonym__remove { - background-color: transparent; - border: none; - color: #a00; - cursor: pointer; - margin: -8px 0 0 10px; - padding: 0; - - & .dashicons-dismiss { - margin: -2px 2px 0 0; - } -} - -.synonym__validation::before { - content: ""; - flex-basis: 100%; - height: 0; -} - -.synonym__validation, -.synonym-solr-editor__validation p { - color: #a00; - font-style: italic; -} - -.synonym__validation { - margin: 0 0 0.625em 0.5em; -} - -.synonym-btn-group button.button { - margin-right: 0.625em; -} diff --git a/lib/elasticpress/assets/js/autosuggest.js b/lib/elasticpress/assets/js/autosuggest.js deleted file mode 100644 index 5ed53da..0000000 --- a/lib/elasticpress/assets/js/autosuggest.js +++ /dev/null @@ -1,782 +0,0 @@ -/* eslint-disable camelcase, no-underscore-dangle, no-use-before-define */ -import { - findAncestorByClass, - escapeDoubleQuotes, - replaceGlobally, - debounce, - domReady, -} from './utils/helpers'; -import 'element-closest'; -import 'promise-polyfill/src/polyfill'; -import 'whatwg-fetch'; - -const { epas } = window; - -// Ensure we have an endpoint URL, or -// else this shouldn't happen -if (epas.endpointUrl && epas.endpointUrl !== '') { - init(); - - // Publically expose API - window.epasAPI = { - hideAutosuggestBox, - updateAutosuggestBox, - esSearch, - buildSearchQuery, - }; -} - -/** - * Submit the search form - * - * @param {Node} input - input element - */ -function submitSearchForm(input) { - input.closest('form').submit(); -} - -/** - * Set the expanded aria state on the input - * - * @param {boolean} haveOptions - whether or not the autosuggest list contains results - * @param {Node} input - search input - */ -function toggleInputAria(haveOptions, input) { - input.setAttribute('aria-expanded', haveOptions); -} - -/** - * Set the active descendant aria attribute input - * - * @param {string} id - id of the currently selected element - * @param {Node} input - search input - */ -function setInputActiveDescendant(id, input) { - input.setAttribute('aria-activedescendant', id); -} - -/** - * Take selected item and fill the search input - * - * @param {Node} input - input element - * @param {string} text - new input value - */ -function selectAutosuggestItem(input, text) { - input.value = text; // eslint-disable-line no-param-reassign -} - -/** - * Fires events when autosuggest results are clicked, - * and if GA tracking is activated - * - * @param {object} detail - value to pass on to the Custom Event - */ -function triggerAutosuggestEvent(detail) { - const event = new CustomEvent('ep-autosuggest-click', { detail }); - window.dispatchEvent(event); - - if ( - detail.searchTerm && - parseInt(epas.triggerAnalytics, 10) === 1 && - typeof gtag === 'function' - ) { - const action = `click - ${detail.searchTerm}`; - // eslint-disable-next-line no-undef - gtag('event', action, { - event_category: 'EP :: Autosuggest', - event_label: detail.url, - transport_type: 'beacon', - }); - } -} - -/** - * Navigate to the selected item, and provides - * event hook for JS customizations, like GA - * - * @param {string} searchTerm - user defined search term - * @param {string} url - post url from dataset in search result - */ -function goToAutosuggestItem(searchTerm, url) { - const detail = { - searchTerm, - url, - }; - - triggerAutosuggestEvent(detail); - window.location.href = url; -} - -/** - * Respond to an item selection based on the predefined behavior. - * If epas.action is set to "navigate" (the default), redirects the browser to the URL of the selected item - * If epas.action is set to any other value (such as "search"), fill in the value and perform the search - * - * @param {Node} input - search input - * @param {Node} element - search term result item - * @returns {Function} calls the submitSearchForm function - */ -function selectItem(input, element) { - if (epas.action === 'navigate') { - return goToAutosuggestItem(input.value, element.dataset.url); - } - - selectAutosuggestItem(input, element.innerText); - return submitSearchForm(input); -} - -/** - * Build the search query from the search text - the query is generated in PHP - * and passed into the front end as window.epas = { "query... - * - * @returns {string} json string - */ -function getJsonQuery() { - if (typeof window.epas === 'undefined') { - const error = 'No epas object defined'; - - // eslint-disable-next-line no-console - console.warn(error); - return { error }; - } - - return window.epas; -} - -/** - * Build the search query from the search text - * - * @param {string} searchText - user search string - * @param {string} placeholder - placeholder text to replace - * @param {object} options - Autosuggest settings - * @param {string} options.query - JSON query string to pass to ElasticSearch - * @returns {string} json representation of search query - */ -function buildSearchQuery(searchText, placeholder, { query }) { - const newQuery = replaceGlobally(query, placeholder, searchText); - return newQuery; -} - -/** - * Build the ajax request - * - * @param {string} query - json string - * @param {string} searchTerm - user search term - * @returns {object} AJAX object request - */ -async function esSearch(query, searchTerm) { - const fetchConfig = { - body: query, - method: 'POST', - mode: 'cors', - headers: { - 'Content-Type': 'application/json; charset=utf-8', - }, - }; - - if (epas?.http_headers && typeof epas.http_headers === 'object') { - Object.keys(epas.http_headers).forEach((name) => { - fetchConfig.headers[name] = epas.http_headers[name]; - }); - } - - // only applies headers if using ep.io endpoint - if (epas.addSearchTermHeader) { - fetchConfig.headers['EP-Search-Term'] = encodeURI(searchTerm); - } - - try { - const response = await window.fetch(epas.endpointUrl, fetchConfig); - if (!response.ok) { - throw Error(response.statusText); - } - - const data = await response.json(); - - // allow for filtered data before returning it to - // be output on the front end - if (typeof window.epDataFilter !== 'undefined') { - return window.epDataFilter(data, searchTerm); - } - - return data; - } catch (error) { - // eslint-disable-next-line no-console - console.error(error); - return error; - } -} - -/** - * Update the auto suggest box with new options or hide if none - * - * @param {Array} options - search results - * @param {string} input - search string - * @returns {boolean} return true - */ -function updateAutosuggestBox(options, input) { - let i; - let itemString = ''; - - // get the search term for use later on - const { value } = input; - const container = findAncestorByClass(input, 'ep-autosuggest-container'); - const resultsContainer = container.querySelector('.ep-autosuggest'); - const suggestList = resultsContainer.querySelector('.autosuggest-list'); - - // empty the the list of all child nodes - while (suggestList.firstChild) { - suggestList.removeChild(suggestList.firstChild); - } - - if (options.length > 0) { - resultsContainer.style = 'display: block;'; - } else { - resultsContainer.style = 'display: none;'; - } - - // anticipating the future... a setting where we configure - // a limit of results to show, and optionally append a - // link to "all results" or something of that nature - const resultsLimit = options.length; - - // create markup for list items - // eslint-disable-next-line - for ( i = 0; resultsLimit > i; ++i ) { - const text = options[i]._source.post_title; - const url = options[i]._source.permalink; - const escapedText = escapeDoubleQuotes(text); - - const searchParts = value.trim().split(' '); - let resultsText = escapedText; - - if (epas.highlightingEnabled) { - // uses some regex magic to match upper/lower/capital case - const regex = new RegExp(`\\b(${searchParts.join('|')})`, 'gi'); - resultsText = resultsText.replace( - regex, - (word) => - `<${epas.highlightingTag} class="${epas.highlightingClass} ep-autosuggest-highlight">${word}`, - ); - } - - let itemHTML = `
  • - - ${resultsText} - -
  • `; - - if (typeof window.epAutosuggestItemHTMLFilter !== 'undefined') { - itemHTML = window.epAutosuggestItemHTMLFilter(itemHTML, options[i], i, value); - } - - itemString += itemHTML; - } - - // append list items to the list - suggestList.innerHTML = itemString; - - const autosuggestItems = Array.from(document.querySelectorAll('.autosuggest-link')); - - suggestList.addEventListener('click', (event) => { - event.preventDefault(); - const target = - event.target.tagName === epas.highlightingTag?.toUpperCase() - ? event.target.parentElement - : event.target; - - if (autosuggestItems.includes(target)) { - selectItem(input, target); - } - }); - - return true; -} - -/** - * Hide the auto suggest box - * - * @returns {boolean} returns true - */ -function hideAutosuggestBox() { - const lists = document.querySelectorAll('.autosuggest-list'); - const containers = document.querySelectorAll('.ep-autosuggest'); - - // empty all EP results lists - lists.forEach((list) => { - while (list.firstChild) { - list.removeChild(list.firstChild); - } - }); - - // hide all EP results containers - containers.forEach((container) => { - // eslint-disable-next-line - container.style = 'display: none;'; - }); - - return true; -} - -/** - * Checks for any manually ordered posts and puts them in the correct place - * - * @param {Array} hits - ES results - * @param {string} searchTerm - user search term - * @returns {object} formatted hits - */ -function checkForOrderedPosts(hits, searchTerm) { - const toInsert = {}; - const taxName = 'ep_custom_result'; - const lowerCaseSearchTerm = searchTerm.toLowerCase(); - - const filteredHits = hits.filter((hit) => { - // Should we retain this hit in its current position? - let retain = true; - - if (undefined !== hit._source.terms && undefined !== hit._source.terms[taxName]) { - hit._source.terms[taxName].forEach((currentTerm) => { - if (currentTerm.name.toLowerCase() === lowerCaseSearchTerm) { - toInsert[currentTerm.term_order] = hit; - - retain = false; - } - }); - } - - return retain; - }); - - const orderedInserts = {}; - - Object.keys(toInsert) - .sort() - .forEach((key) => { - orderedInserts[key] = toInsert[key]; - }); - - if (Object.keys(orderedInserts).length > 0) { - Object.keys(orderedInserts).forEach((key) => { - const insertItem = orderedInserts[key]; - - filteredHits.splice(key - 1, 0, insertItem); - }); - } - - return filteredHits; -} - -/** - * Add class to the form element while suggestions are being loaded - * - * @param {boolean} isLoading - whether suggestions are loading - * @param {Node} input - search input field - */ -function setFormIsLoading(isLoading, input) { - const form = input.closest('form'); - - if (isLoading) { - form.classList.add('is-loading'); - } else { - form.classList.remove('is-loading'); - } -} - -/** - * init method called if the epas endpoint is defined - */ -function init() { - const selectors = [epas.defaultSelectors, epas.selector].filter(Boolean).join(','); - - if (!selectors) { - return; - } - - // For the Autosuggest element that will be cloned. - let autosuggestElement; - - // to be used by the handleUpDown function - // to keep track of the currently selected result - let currentIndex; - - // these are the keycodes we listen for in handleUpDown, - // and in handleKeyup - const keyCodes = [ - 38, // up - 40, // down - 13, // enter - ]; - - /** - * Handles keyup event on the search input - * - * @param {event} event - keyup event - */ - const handleUpDown = (event) => { - if (!keyCodes.includes(event.keyCode)) { - return; - } - - const input = event.target; - const container = findAncestorByClass(input, 'ep-autosuggest-container'); - const suggestList = container.querySelector('.autosuggest-list'); - const results = suggestList.children; - - /** - * helper function to get the currently selected result - * - * @returns {number} index of the selected search result - */ - const getSelectedResultIndex = () => { - const resultsArr = Array.from(results); - return resultsArr.findIndex((result) => result.classList.contains('selected')); - }; - - /** - * helper function to deselect results - */ - const deSelectResults = () => { - Array.from(results).forEach((result) => { - result.classList.remove('selected'); - result.setAttribute('aria-selected', 'false'); - }); - }; - - /** - * helper function to selected the next result - */ - const selectNextResult = () => { - if (currentIndex >= 0) { - const el = results[currentIndex]; - el.classList.add('selected'); - el.setAttribute('aria-selected', 'true'); - setInputActiveDescendant(el.id, input); - } - }; - - // select next or previous based on keyCode - // if enter, navigate to that element - switch (event.keyCode) { - case 38: // Up - // don't go less than the 0th index - currentIndex = currentIndex - 1 >= 0 ? currentIndex - 1 : 0; - deSelectResults(); - break; - case 40: // Down - if (typeof currentIndex === 'undefined') { - // index is not yet defined, so let's - // start with the first one - currentIndex = 0; - } else { - const current = getSelectedResultIndex(); - - // check for existence of next result - if (results[current + 1]) { - currentIndex = current + 1; - deSelectResults(); - } - } - break; - case 13: // Enter - if (results[currentIndex].classList.contains('selected')) { - // navigate to the item defined in the span's data-url attribute - selectItem(input, results[currentIndex].querySelector('.autosuggest-link')); - } - break; - default: - // No item selected - break; - } - - // only check next element if up and down key pressed - if (results[currentIndex] && results[currentIndex].classList.contains('autosuggest-item')) { - selectNextResult(); - } else { - deSelectResults(); - } - - // keep cursor from heading back to the beginning in the input - if (event.keyCode === 38) { - // return false; - event.preventDefault(); - } - }; - - /** - * Get the searched post types from the search form. - * - * @param {HTMLFormElement} form - form containing the search input field - * @returns {Array} - post types - * @since 3.6.0 - */ - function getPostTypesFromForm(form) { - const data = new FormData(form); - - if (data.has('post_type')) { - return data.getAll('post_type').slice(-1); - } - - if (data.has('post_type[]')) { - return data.getAll('post_type[]'); - } - - return []; - } - - /** - * Calls the ajax request, and outputs the results. - * Called by the handleKeyup callback, debounced. - * - * @param {Node} input - search input field - */ - const fetchResults = async (input) => { - // retrieves the PHP-genereated query to pass to ElasticSearch - const queryJSON = getJsonQuery(); - - if (queryJSON.error) { - return; - } - - const searchText = input.value; - const placeholder = 'ep_autosuggest_placeholder'; - const postTypes = getPostTypesFromForm(input.form); - - if (searchText.length >= 2) { - setFormIsLoading(true, input); - - let query = buildSearchQuery(searchText, placeholder, queryJSON); - - if (postTypes.length > 0) { - query = JSON.parse(query); - - if (typeof query.post_filter.bool.must !== 'undefined') { - query.post_filter.bool.must.push({ - terms: { - 'post_type.raw': postTypes, - }, - }); - } - - query = JSON.stringify(query); - } - - // fetch the results - const response = await esSearch(query, searchText); - - if (response && response._shards && response._shards.successful > 0) { - const hits = checkForOrderedPosts(response.hits.hits, searchText); - - if (hits.length === 0) { - hideAutosuggestBox(); - } else { - updateAutosuggestBox(hits, input); - } - } else { - hideAutosuggestBox(); - } - - setFormIsLoading(false, input); - } else if (searchText.length === 0) { - hideAutosuggestBox(); - } - }; - - const debounceFetchResults = debounce(fetchResults, 200); - - /** - * Callback for keyup in Autosuggest container. - * - * Calls a debounced function to get the search results via - * ajax request. - * - * @param {event} event - keyup event - */ - const handleKeyup = (event) => { - event.preventDefault(); - const { target, key, keyCode } = event; - - if (key === 'Escape' || key === 'Esc' || keyCode === 27) { - hideAutosuggestBox(); - toggleInputAria(false, target); - setInputActiveDescendant('', target); - return; - } - - if (keyCodes.includes(keyCode) && target.value !== '') { - handleUpDown(event); - return; - } - - const input = event.target; - debounceFetchResults(input); - }; - - /** - * Wrap an element with an autosuggest container. - * - * @param {Element} element Element to wrap. - * @returns {void} - */ - const wrapInAutosuggestContainer = (element) => { - const epContainer = document.createElement('div'); - - epContainer.classList.add('ep-autosuggest-container'); - - element.insertAdjacentElement('afterend', epContainer); - - epContainer.appendChild(element); - }; - - /** - * Insert an autosuggest list after an element. - * - * @param {Element} element Element to add the autosuggest list after. - * @returns {void} - */ - const insertAutosuggestElement = (element) => { - if (!autosuggestElement) { - autosuggestElement = document.createElement('div'); - autosuggestElement.classList.add('ep-autosuggest'); - - const autosuggestList = document.createElement('ul'); - - autosuggestList.classList.add('autosuggest-list'); - autosuggestList.setAttribute('role', 'listbox'); - - autosuggestElement.appendChild(autosuggestList); - } - - const clonedElement = autosuggestElement.cloneNode(true); - - element.insertAdjacentElement('afterend', clonedElement); - }; - - /** - * Prepare an input for Autosuggest. - * - * @param {Element} input Input to prepare. - * @returns {void} - */ - const prepareInputForAutosuggest = (input) => { - /** - * Skip facet widget search fields. - */ - if (input.classList.contains('facet-search')) { - return; - } - - /** - * Disable autocomplete. - */ - input.setAttribute('autocomplete', 'off'); - - /** - * We know the markup of the Search block, so we don't need to add a - * wrapper. - */ - if (input.classList.contains('wp-block-search__input')) { - input.form.classList.add('ep-autosuggest-container'); - insertAutosuggestElement(input.parentElement); - } else { - wrapInAutosuggestContainer(input); - insertAutosuggestElement(input); - } - - /** - * Dispatch an event announcing the input has moved. - */ - const event = new CustomEvent('elasticpress.input.moved'); - - input.dispatchEvent(event); - - /** - * Listen for any events: - * - * keyup - * send them for a query to the Elasticsearch server - * handle up and down keys to move between results - * - * blur - * hide the autosuggest box - */ - input.addEventListener('keyup', handleKeyup); - input.addEventListener('blur', function () { - window.setTimeout(hideAutosuggestBox, 200); - }); - }; - - /** - * Find inputs within an element and prepare them for Autosuggest. - * - * @param {Element} element Element to find inputs within. - * @returns {void} - */ - const findAndPrepareInputsForAutosuggest = (element) => { - const inputs = element.querySelectorAll(selectors); - - if (inputs) { - Array.from(inputs).forEach(prepareInputForAutosuggest); - } - }; - - /** - * Observe the document for new potential Autosuggest inputs, and add - * Autosuggest to any found inputs. - * - * @returns {void} - */ - const observeDocumentForInputs = () => { - const target = document.body; - const config = { - subtree: true, - childList: true, - }; - - const observer = new MutationObserver((mutations, observer) => { - mutations.forEach((mutation) => { - Array.from(mutation.addedNodes).forEach((node) => { - if (node.nodeType !== Node.ELEMENT_NODE) { - return; - } - - /** - * Adding autosuggest to an input moves it in the DOM, - * which would trigger our observer, so we need to - * stop observing until it's been prepared. - */ - observer.disconnect(); - - /** - * If the node is an input, prepare it for Autosuggest if - * it matches the selectors, otherwise search the node for - * inputs. - */ - if (node.tagName === 'INPUT') { - if (node.matches(selectors)) { - prepareInputForAutosuggest(node); - } - } else { - findAndPrepareInputsForAutosuggest(node); - } - - /** - * Resume observing. - */ - observer.observe(target, config); - }); - }); - }); - - observer.observe(target, config); - }; - - /** - * Add autosuggest to any inputs in the document. - */ - findAndPrepareInputsForAutosuggest(document.body); - - /** - * When the DOM is ready start observing for new inputs. - */ - domReady(observeDocumentForInputs); -} diff --git a/lib/elasticpress/assets/js/blocks/related-posts/Edit.js b/lib/elasticpress/assets/js/blocks/related-posts/Edit.js deleted file mode 100644 index c734faa..0000000 --- a/lib/elasticpress/assets/js/blocks/related-posts/Edit.js +++ /dev/null @@ -1,106 +0,0 @@ -const { __ } = wp.i18n; - -const { AlignmentToolbar, BlockControls, InspectorControls } = wp.editor; - -const { PanelBody, Placeholder, Spinner, QueryControls } = wp.components; - -const { Fragment, Component, RawHTML } = wp.element; - -const { addQueryArgs } = wp.url; - -/** - * Edit component - */ -class Edit extends Component { - /** - * Setup class - * - * @param {object} props Component properties - */ - constructor(props) { - super(props); - - this.state = { - posts: false, - }; - } - - /** - * Load preview data - */ - componentDidMount() { - const urlArgs = { - number: 100, - }; - - // Use 0 if in the Widgets Screen - const postId = wp.data.select('core/editor').getCurrentPostId() ?? 0; - - wp.apiFetch({ - path: addQueryArgs(`/wp/v2/posts/${postId}/related`, urlArgs), - }) - .then((posts) => { - this.setState({ posts }); - }) - .catch(() => { - this.setState({ posts: false }); - }); - } - - render() { - const { - attributes: { alignment, number }, - setAttributes, - className, - } = this.props; - const { posts } = this.state; - - const displayPosts = posts.length > number ? posts.slice(0, number) : posts; - - return ( - - - setAttributes({ alignment: newValue })} - /> - - - - setAttributes({ number: value })} - /> - - - -
    - {displayPosts === false || displayPosts.length === 0 ? ( - - {posts === false ? : __('No related posts yet.')} - - ) : ( - - )} -
    -
    - ); - } -} - -export default Edit; diff --git a/lib/elasticpress/assets/js/blocks/related-posts/block.js b/lib/elasticpress/assets/js/blocks/related-posts/block.js deleted file mode 100644 index 647c8f9..0000000 --- a/lib/elasticpress/assets/js/blocks/related-posts/block.js +++ /dev/null @@ -1,40 +0,0 @@ -import Edit from './Edit'; - -const { registerBlockType } = wp.blocks; - -registerBlockType('elasticpress/related-posts', { - title: 'Related Posts (ElasticPress)', - supports: { - align: true, - }, - category: 'widgets', - attributes: { - alignment: { - type: 'string', - default: 'none', - }, - number: { - type: 'number', - default: 5, - }, - }, - - /** - * Handle edit - * - * @param {object} props Component properties - * @returns {object} - */ - edit(props) { - return ; - }, - - /** - * Handle save - * - * @returns {void} - */ - save() { - return null; - }, -}); diff --git a/lib/elasticpress/assets/js/comments.js b/lib/elasticpress/assets/js/comments.js deleted file mode 100644 index e59440a..0000000 --- a/lib/elasticpress/assets/js/comments.js +++ /dev/null @@ -1,288 +0,0 @@ -import { findAncestorByClass, debounce } from './utils/helpers'; - -const widgetSearchComments = document.querySelectorAll('.ep-widget-search-comments'); - -let selectedResultIndex; - -widgetSearchComments.forEach((element) => { - const input = document.createElement('input'); - input.setAttribute('autocomplete', 'off'); - input.setAttribute('type', 'search'); - input.setAttribute('class', 'ep-widget-search-comments-input'); - - const resultList = document.createElement('ul'); - resultList.setAttribute('class', 'ep-widget-search-comments-results'); - - element.appendChild(input); - element.appendChild(resultList); -}); - -// these are the keycodes we listen for in handleUpDown, -// and in handleKeyup -const keyCodes = [ - 38, // up - 40, // down - 13, // enter -]; - -/** - * Hide the result list - * - * @param {HTMLInputElement} inputElement The input element used in the widget - */ -function hideResultsBox(inputElement) { - selectedResultIndex = undefined; - - const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments'); - const resultList = widget.querySelector('.ep-widget-search-comments-results'); - - while (resultList.firstChild) { - resultList.removeChild(resultList.firstChild); - } -} - -/** - * Update the result list - * - * @param {object} comments Comments to be showed - * @param {HTMLInputElement} inputElement The input element used in the widget - */ -const updateResultsBox = (comments, inputElement) => { - let items = ''; - let itemHTML = ''; - - Object.keys(comments).forEach((id, index) => { - if (comments[id]?.content && comments[id]?.link) { - itemHTML = ` -
  • - - ${comments[id].content} - -
  • - `; - - if (typeof window.epCommentWidgetItemHTMLFilter !== 'undefined') { - itemHTML = window.epCommentWidgetItemHTMLFilter( - itemHTML, - comments[id], - index, - inputElement.value, - ); - } - - items += itemHTML; - } - }); - - const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments'); - const resultList = widget.querySelector('.ep-widget-search-comments-results'); - - if (typeof window.epCommentWidgetItemsHTMLFilter !== 'undefined') { - items = window.epCommentWidgetItemsHTMLFilter(items, inputElement.value); - } - - resultList.innerHTML = items; -}; - -/** - * Update the result list to inform the user that no results were found - * - * @param {HTMLInputElement} inputElement The input element used in the widget - */ -const showNotFoundInResultsBox = (inputElement) => { - const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments'); - const resultList = widget.querySelector('.ep-widget-search-comments-results'); - - let itemHTML = `
  • ${window.epc.noResultsFoundText}
  • `; - - if (typeof window.epCommentWidgetItemNotFoundHTMLFilter !== 'undefined') { - itemHTML = window.epCommentWidgetItemNotFoundHTMLFilter( - itemHTML, - window.epc.noResultsFoundText, - inputElement.value, - ); - } - - resultList.innerHTML = itemHTML; -}; - -function hasMinimumLength(inputElement) { - const minimumLength = window.epc.minimumLengthToSearch || 2; - return inputElement?.value?.trim().length >= minimumLength; -} - -/** - * Add class to the widget element while results are being loaded - * - * @param {boolean} isLoading Whether results are loading - * @param {Node} inputElement Search input field - */ -function setIsLoading(isLoading, inputElement) { - const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments'); - - if (isLoading) { - widget.classList.add('ep-widget-search-comments-is-loading'); - } else { - widget.classList.remove('ep-widget-search-comments-is-loading'); - } -} - -/** - * Fetch comments - * - * @param {HTMLInputElement} inputElement The input element used in the widget - * @returns {(false|Promise)} Try to fetch comments - */ -function fetchResults(inputElement) { - if (hasMinimumLength(inputElement)) { - const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments'); - const postTypeElement = widget.querySelector('#ep-widget-search-comments-post-type'); - const postTypeQueryParameter = postTypeElement?.value - ? `&post_type=${postTypeElement.value.trim()}` - : ''; - - setIsLoading(true, inputElement); - return fetch( - `${window.epc.restApiEndpoint}?s=${inputElement.value.trim()}${postTypeQueryParameter}`, - ) - .then((response) => { - if (!response.ok) { - throw response; - } - - return response.json(); - }) - .then((comments) => { - if (Object.keys(comments).length === 0) { - if (inputElement.value.trim()) { - showNotFoundInResultsBox(inputElement); - } else { - hideResultsBox(inputElement); - } - } else { - updateResultsBox(comments, inputElement); - } - }) - .catch(() => { - hideResultsBox(inputElement); - }) - .finally(() => { - setIsLoading(false, inputElement); - }); - } - return false; -} - -/** - * Handle up, down and enter key - * - * @param {Event} event keyup event - */ -const handleUpDownEnter = (event) => { - if (!keyCodes.includes(event.keyCode)) { - return; - } - - const widget = findAncestorByClass(event.target, 'ep-widget-search-comments'); - const resultList = widget.querySelector('.ep-widget-search-comments-results'); - const sizeResult = resultList.querySelectorAll('.ep-widget-search-comments-result-item').length; - const results = resultList.children; - - const previousSelectedResultIndex = selectedResultIndex; - - switch (event.keyCode) { - case 38: // Up - selectedResultIndex = - selectedResultIndex - 1 < 0 || typeof selectedResultIndex === 'undefined' - ? sizeResult - 1 - : selectedResultIndex - 1; - - break; - - case 40: // Down - if ( - typeof selectedResultIndex === 'undefined' || - selectedResultIndex + 1 > sizeResult - 1 - ) { - selectedResultIndex = 0; - } else { - selectedResultIndex += 1; - } - - break; - - case 13: // Enter - if (results[selectedResultIndex]?.classList.contains('selected') || sizeResult === 1) { - const indexItem = selectedResultIndex || 0; - - if (results[indexItem]) { - const linkToComment = results[indexItem] - .querySelector('a') - ?.getAttribute('href'); - - window.location.href = linkToComment; - } - } - - break; - - default: - break; - } - - if (typeof previousSelectedResultIndex === 'number') { - results[previousSelectedResultIndex].classList.remove('selected'); - results[previousSelectedResultIndex].setAttribute('aria-selected', 'false'); - } - - results[selectedResultIndex]?.classList.add('selected'); - results[selectedResultIndex]?.setAttribute('aria-selected', 'true'); -}; - -const debounceFetchResults = debounce(fetchResults, 500); - -/** - * Callback for keyup in Widget Search Comment container. - * - * Calls a debounced function to get the search results via - * api rest request. - * - * @param {event} event - keyup event - */ -const handleKeyup = (event) => { - event.preventDefault(); - const { target, key, keyCode } = event; - - if (key === 'Escape' || key === 'Esc' || keyCode === 27) { - hideResultsBox(target); - target.setAttribute('aria-expanded', false); - - return; - } - - if (keyCodes.includes(keyCode) && target.value !== '') { - handleUpDownEnter(event); - - return; - } - - if (hasMinimumLength(target)) { - debounceFetchResults(target); - } else { - hideResultsBox(target); - } -}; - -widgetSearchComments.forEach((element) => { - const input = element.querySelector('.ep-widget-search-comments-input'); - - input.addEventListener('keyup', handleKeyup); - input.addEventListener('keydown', (event) => { - if (event.keyCode === 38) { - event.preventDefault(); - } - }); - input.addEventListener('blur', function () { - setTimeout(() => hideResultsBox(input), 200); - }); -}); diff --git a/lib/elasticpress/assets/js/dashboard.js b/lib/elasticpress/assets/js/dashboard.js deleted file mode 100644 index b7f0b1f..0000000 --- a/lib/elasticpress/assets/js/dashboard.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Internal dependencies. - */ -import Tooltip from '@10up/component-tooltip'; - -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -/** - * Window dependencies. - */ -const { - ajaxurl, - epDash: { syncUrl }, -} = window; - -/** - * Determine whether a Feature's new settings will require a reindex. - * - * @param {FormData} data Form data. - * @returns {boolean} Whether a reindex will need to occur when saved. - */ -const willChangeTriggerReindex = (data) => { - return ( - data.get('requires_reindex') === '1' && - data.get('was_active') === '0' && - data.get('settings[active]') === '1' - ); -}; - -/** - * Handle Feature settings being submitted. - * - * @param {Event} event Submit event. - */ -const onSubmit = async (event) => { - event.preventDefault(); - - const form = event.target; - const data = new FormData(form); - const requiresConfirmation = willChangeTriggerReindex(data); - - if (requiresConfirmation) { - /* eslint-disable no-alert */ - const isConfirmed = window.confirm( - __( - 'Enabling this feature will begin re-indexing your content. Do you wish to proceed?', - 'elasticpress', - ), - ); - - if (!isConfirmed) { - return; - } - } - - const feature = form.closest('.ep-feature'); - - feature.classList.add('saving'); - form.submit.disabled = true; - - const request = await fetch(ajaxurl, { method: 'POST', body: data }); - const response = await request.json(); - - feature.classList.toggle('feature-active', response.data.active); - - if (response.data.reindex) { - window.location = syncUrl; - } else { - feature.classList.remove('saving'); - form.submit.disabled = false; - form.was_active.value = response.data.active ? '1' : '0'; - } -}; - -/** - * Handle a Feature being set to be turned on or off. - * - * @param {Event} event Change event. - */ -const onToggle = (event) => { - const { form } = event.target; - const data = new FormData(form); - - const notice = form.querySelector('.requirements-status-notice--reindex'); - const requiresConfirmation = willChangeTriggerReindex(data); - - if (notice) { - notice.style.display = requiresConfirmation ? 'block' : null; - } -}; - -/** - * Handle click events within a Feature. - * - * @param {Event} event Click event. - */ -const onClick = (event) => { - const { target } = event; - - /** - * Handle toggling settings. - */ - if (target.classList.contains('settings-button')) { - const feature = target.closest('.ep-feature'); - - feature.classList.toggle('show-settings'); - target.setAttribute('aria-expanded', feature.classList.contains('show-settings')); - } - - /** - * Handle toggling description. - */ - if (target.classList.contains('learn-more') || target.classList.contains('collapse')) { - target.closest('.ep-feature').classList.toggle('show-full'); - } -}; - -/** - * Bind events. - */ -const featuresEl = document.querySelector('.ep-features'); - -if (featuresEl) { - featuresEl.addEventListener('change', onToggle); - featuresEl.addEventListener('submit', onSubmit); - featuresEl.addEventListener('click', onClick); -} - -/** - * Tooltips. - */ -// eslint-disable-next-line no-new -new Tooltip('.a11y-tip'); diff --git a/lib/elasticpress/assets/js/facets.js b/lib/elasticpress/assets/js/facets.js deleted file mode 100644 index daa4ac6..0000000 --- a/lib/elasticpress/assets/js/facets.js +++ /dev/null @@ -1,52 +0,0 @@ -import { debounce } from './utils/helpers'; - -/** - * Filters the facets to match the input search term when - * the number of terms exceeds the threshold determined - * by the ep_facet_search_threshold filter - * - * @param {event} event - keyup - * @param {Node} facetTerms - terms node - */ -const handleFacetSearch = (event, facetTerms) => { - const { target } = event; - const searchTerm = target.value.toLowerCase(); - const terms = facetTerms.querySelectorAll('.term'); - - terms.forEach((term) => { - const slug = term.getAttribute('data-term-slug'); - const name = term.getAttribute('data-term-name'); - - if (name.includes(searchTerm) || slug.includes(searchTerm)) { - term.classList.remove('hide'); - } else { - term.classList.add('hide'); - } - }); -}; - -/** - * Filter facet choices to match the search field term - */ -const facets = document.querySelectorAll('.widget_ep-facet'); - -facets.forEach((facet) => { - const facetSearchInput = facet.querySelector('.facet-search'); - - if (!facetSearchInput) { - return; - } - - const facetTerms = facet.querySelector('.terms'); - - facet.querySelector('.facet-search').addEventListener( - 'keyup', - debounce((event) => { - if (event.keyCode === 13) { - return; - } - - handleFacetSearch(event, facetTerms); - }, 200), - ); -}); diff --git a/lib/elasticpress/assets/js/instant-results/admin/components/facet-selector.js b/lib/elasticpress/assets/js/instant-results/admin/components/facet-selector.js deleted file mode 100644 index bdcb2ae..0000000 --- a/lib/elasticpress/assets/js/instant-results/admin/components/facet-selector.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * WordPress dependencies. - */ -import { FormTokenField } from '@wordpress/components'; -import { useMemo, useState, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import { facets } from '../config'; - -/** - * Facet selector component. - * - * @param {object} props Props. - * @param {string} props.defaultValue Default value. - * @returns {WPElement} Element. - */ -export default ({ defaultValue, ...props }) => { - const defaultValues = defaultValue.split(','); - const [selectedFacets, setSelectedFacets] = useState(defaultValues); - - /** - * Get the label for a facet from the facet key. - * - * @param {string} key Facet key. - * @returns {string} Facet label. - */ - const getLabelFromKey = (key) => { - return facets[key]?.label; - }; - - /** - * Get the key for a facet from the facet label. - * - * @param {string} label Facet label. - * @returns {string} Facet key. - */ - const getKeyFromLabel = (label) => { - return Object.keys(facets).find((key) => { - return label === facets[key].label; - }); - }; - - /** - * Suggestions for the token field. - */ - const suggestions = useMemo(() => Object.keys(facets).map(getLabelFromKey).filter(Boolean), []); - - /** - * Values for the token field. - */ - const value = useMemo( - () => selectedFacets.map(getLabelFromKey).filter(Boolean), - [selectedFacets], - ); - - /** - * Handle change to token field. - * - * @param {Array} tokens Selected tokens. - */ - const onChange = (tokens) => { - setSelectedFacets(tokens.map(getKeyFromLabel).filter(Boolean)); - }; - - return ( - <> - - - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/admin/config.js b/lib/elasticpress/assets/js/instant-results/admin/config.js deleted file mode 100644 index ba3d3fb..0000000 --- a/lib/elasticpress/assets/js/instant-results/admin/config.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Window dependencies. - */ -const { facets } = window.epInstantResultsAdmin; - -export { facets }; diff --git a/lib/elasticpress/assets/js/instant-results/admin/index.js b/lib/elasticpress/assets/js/instant-results/admin/index.js deleted file mode 100644 index 781fb57..0000000 --- a/lib/elasticpress/assets/js/instant-results/admin/index.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * WordPress dependencies. - */ -import { render } from '@wordpress/element'; - -/** - * Internal dependences. - */ -import FacetSelector from './components/facet-selector'; - -document.addEventListener('DOMContentLoaded', () => { - const input = document.getElementById('feature_instant_results_facets'); - - const { - className, - dataset: { fieldName }, - id, - name, - value, - } = input; - - render( - , - input.parentElement, - ); -}); diff --git a/lib/elasticpress/assets/js/instant-results/components/common/checkbox-list.js b/lib/elasticpress/assets/js/instant-results/components/common/checkbox-list.js deleted file mode 100644 index 9a0fbb8..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/common/checkbox-list.js +++ /dev/null @@ -1,240 +0,0 @@ -/** - * WordPress dependencies. - */ -import { Fragment, useRef, useState, WPElement } from '@wordpress/element'; -import { __, _n, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import Checkbox from './checkbox'; -import SmallButton from './small-button'; - -/** - * Checkbox list component. - * - * @typedef {object} Option - * @property {number} count Number associated with option. - * @property {string} id Option ID. - * @property {string} label Option label. - * @property {number} order Option order. - * @property {string} parent Parent option value. - * @property {any} value Option value. - * - * @param {object} props Component props. - * @param {boolean} props.disabled Whether the checkboxes should be disabled. - * @param {boolean} props.label List label. - * @param {Function} props.onChange Checkbox change event callback function. - * @param {Option[]} props.options Checkbox options. - * @param {string} props.selected Selected values. - * @param {string} props.sortBy How to sort options. - * @returns {WPElement} A React element. - */ -export default ({ disabled, label, options, onChange, selected, sortBy }) => { - /** - * Outermost list element. - */ - const listEl = useRef(null); - - /** - * Whether all items are displayed and a setter. - */ - const [showAll, setShowAll] = useState(false); - - /** - * Reducer to group options by parent. - * - * @param {object} items Options grouped by parent. - * @param {Option} option Option details. - * @param {string} option.parent Option parent value. - * @returns {object} Options grouped by parent. - */ - const reduceOptionsByParent = (items, { parent, ...option }) => { - // eslint-disable-next-line eqeqeq - if (parent != false) { - items[parent] = items[parent] || []; - items[parent].push(option); - } - - return items; - }; - - /** - * Options grouped by parent value. - */ - const childOptions = options.reduce(reduceOptionsByParent, {}); - - /** - * Reducer to group top level options. Top level options are options - * with a parent of '0' or without a parent. - * - * @param {Array} items Options without a parent. - * @param {Option} option Option details. - * @param {string} option.parent Option parent value. - * @returns {object} Options without a parent. - */ - const reduceTopLevelOptions = (items, { parent, ...option }) => { - // eslint-disable-next-line eqeqeq - if (parent == false || parent === '0') { - items.push(option); - } - - return items; - }; - - /** - * Top level options. - */ - const topLevelOptions = options.reduce(reduceTopLevelOptions, []); - - /** - * How many options should be displayed by default. - */ - const optionsLimit = options.length > 8 ? 5 : 8; - - /** - * How many options have been displayed. - * - * Incremented each time an item is displayed. Used to limit the number of - * items displayed by default until the show more button is pressed. - */ - let optionsShown = 0; - - /** - * Handle checkbox change event. - * - * @param {Event} event Change event. - */ - const onCheck = (event) => { - const { checked, value } = event.target; - - let values = checked ? [...selected, value] : selected.filter((v) => v !== value); - - /* Only send selected values that are in the available options. */ - values = values.filter((v) => options.some((o) => o.value === v)); - - onChange(values); - }; - - /** - * Render an option. - * - * @param {Option} option Option. - * @returns {WPElement} Render function. - */ - const displayOption = ({ count, id, label, value }) => { - const children = childOptions[value]; - - if (!showAll && optionsShown >= optionsLimit) { - return ; - } - - const option = ( -
  • - - - {children && (showAll || optionsShown < optionsLimit) && ( -
      - { - /* eslint-disable-next-line no-use-before-define */ - displayOptions(children) - } -
    - )} -
  • - ); - - optionsShown++; - - return option; - }; - - /** - * Sort option callback. - * - * @param {Option} a First option to compare. - * @param {Option} b second option to compare. - * @returns {number} Comparison number. - */ - const sortOptions = (a, b) => { - let comparison = 0; - - if (sortBy === 'count') { - comparison = b.count - a.count; - } - - if (sortBy === 'name' || comparison === 0) { - comparison = a.label.localeCompare(b.label); - } - - return comparison; - }; - - /** - * Render a list of options. - * - * @param {Option[]} options Options to display. - * @returns {WPElement[]} Array of elements. - */ - const displayOptions = (options) => { - return options.splice(0).sort(sortOptions).map(displayOption); - }; - - /** - * Handle clicking the show more/fewer button. - * - * @returns {void} - */ - const onToggleShowAll = () => { - setShowAll(!showAll); - - listEl.current.focus(); - }; - - return ( - <> - {options.length > 0 && ( -
      - { - /* Display top level options and their children. */ - displayOptions(topLevelOptions) - } - { - /* Display remaining orphaned options. */ - Object.values(childOptions).map(displayOptions) - } -
    - )} - - {options.length > optionsLimit && ( - - {showAll - ? __('Show fewer options', 'elasticpress') - : sprintf( - /* translators: %d: Number of additional options available. */ - _n( - 'Show %d more option', - 'Show %d more options', - options.length - optionsLimit, - 'elasticpress', - ), - options.length - optionsLimit, - )} - - )} - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/common/checkbox.js b/lib/elasticpress/assets/js/instant-results/components/common/checkbox.js deleted file mode 100644 index 289c6b9..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/common/checkbox.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * WordPress dependencies. - */ -import { WPElement } from '@wordpress/element'; - -/** - * Checkbox component. - * - * @param {Option} props Component props. - * @param {string} props.count Checkbox count. - * @param {string} props.id Checkbox ID. - * @param {string} props.label Checkbox label. - * - * @returns {WPElement} Component element. - */ -export default ({ count, id, label, ...props }) => { - return ( -
    - {' '} - -
    - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/common/image.js b/lib/elasticpress/assets/js/instant-results/components/common/image.js deleted file mode 100644 index c4fcd9a..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/common/image.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * WordPress dependencies. - */ -import { WPElement } from '@wordpress/element'; - -/** - * Image component. - * - * @param {Option} props Component props. - * - * @returns {WPElement} Component element. - */ -export default ({ alt, height, ID, src, width, ...props }) => { - return {alt}; -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/common/modal.js b/lib/elasticpress/assets/js/instant-results/components/common/modal.js deleted file mode 100644 index 8e96a38..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/common/modal.js +++ /dev/null @@ -1,106 +0,0 @@ -/** - * External dependencies. - */ -import FocusTrap from 'focus-trap-react'; - -/** - * WordPress dependencies. - */ -import { forwardRef, useCallback, useEffect, useRef, WPElement } from '@wordpress/element'; -import { closeSmall, Icon } from '@wordpress/icons'; -import { __ } from '@wordpress/i18n'; - -/** - * Modal components. - * - * @param {object} props Component props. - * @param {WPElement} props.children Component children. - * @param {boolean} props.isOpen Whether the modal is open. - * @param {Function} props.onClose Callback to run when modal is closed. - * @param {object} ref Ref. - * @returns {WPElement} React element. - */ -const Modal = ({ children, isOpen, onClose, ...props }, ref) => { - /** - * Reference to close button element. - */ - const closeRef = useRef(null); - - /** - * Handle key down. - * - * @param {Event} event Keydown event. - */ - const onKeyDown = useCallback( - (event) => { - if (event.key === 'Escape' || event.key === 'Esc') { - onClose(); - } - }, - [onClose], - ); - - /** - * Handle binding events to outside DOM elements. - * - * @returns {Function} Clean up function that removes events. - */ - const handleEvents = () => { - const { current: modalEl } = ref; - - modalEl.ownerDocument.body.addEventListener('keydown', onKeyDown); - - return () => { - modalEl.ownerDocument.body.removeEventListener('keydown', onKeyDown); - }; - }; - - /** - * Handle the model being opened or closed. - * - * Adds a class to the body element to allow controlling scrolling. - */ - const handleOpen = () => { - const { current: modalEl } = ref; - - if (isOpen) { - modalEl.ownerDocument.body.classList.add('has-ep-search-modal'); - closeRef.current.focus(); - } else { - modalEl.ownerDocument.body.classList.remove('has-ep-search-modal'); - } - }; - - useEffect(handleEvents, [onKeyDown, ref]); - useEffect(handleOpen, [isOpen, ref]); - - return ( -
    - {isOpen && ( - -
    - - {children} -
    -
    - )} -
    - ); -}; - -export default forwardRef(Modal); diff --git a/lib/elasticpress/assets/js/instant-results/components/common/panel.js b/lib/elasticpress/assets/js/instant-results/components/common/panel.js deleted file mode 100644 index 11a20b6..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/common/panel.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useState, WPElement } from '@wordpress/element'; -import { chevronDown, chevronUp, Icon } from '@wordpress/icons'; - -/** - * Facet wrapper component. - * - * @param {object} props Component props. - * @param {WPElement} props.children Component children. - * @param {boolean} props.defaultIsOpen Whether the panel is open by default. - * @param {string} props.label Facet label. - * @returns {WPElement} Component element. - */ -export default ({ children, defaultIsOpen, label }) => { - const [isOpen, setIsOpen] = useState(defaultIsOpen); - - /** - * Handle click event on the header. - */ - const onClick = () => { - setIsOpen(!isOpen); - }; - - return ( -
    -

    - -

    -
    - {children(isOpen)} -
    -
    - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/common/range-slider.js b/lib/elasticpress/assets/js/instant-results/components/common/range-slider.js deleted file mode 100644 index 1ee67e9..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/common/range-slider.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * External dependencies. - */ -import ReactSlider from 'react-slider'; - -/** - * WordPress dependencies. - */ -import { WPElement } from '@wordpress/element'; - -/** - * Range slider component. - * - * @param {object} props Props. - * @returns {WPElement} Element. - */ -export default ({ ...props }) => { - return ( - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/common/small-button.js b/lib/elasticpress/assets/js/instant-results/components/common/small-button.js deleted file mode 100644 index 71ccd7e..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/common/small-button.js +++ /dev/null @@ -1,17 +0,0 @@ -import { WPElement } from '@wordpress/element'; - -/** - * Small button component. - * - * @param {object} props Props. - * @param {WPElement} props.children Children. - * @param {string} props.className Class attribute. - * @returns {WPElement} Element. - */ -export default ({ children, className, ...props }) => { - return ( - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/common/star-rating.js b/lib/elasticpress/assets/js/instant-results/components/common/star-rating.js deleted file mode 100644 index 8685ed6..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/common/star-rating.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __, sprintf } from '@wordpress/i18n'; -import { WPElement } from '@wordpress/element'; - -/** - * Star rating component. - * - * @param {Option} props Component props. - * @param {string} props.rating Rating. - * - * @returns {WPElement} Component element. - */ -export default ({ rating }) => { - const label = sprintf( - /* translators: %1$f Rating. %2$d Max rating. */ - __('Rated %1$f out of %2$d', 'elasticpress'), - rating, - 5, - ); - - return ( -
    -
    - {label} -
    -
    - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/facets/facet.js b/lib/elasticpress/assets/js/instant-results/components/facets/facet.js deleted file mode 100644 index 8c3a40f..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/facets/facet.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * WordPress dependencies. - */ -import { WPElement } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import PostTypeFacet from './post-type-facet'; -import PriceRangeFacet from './price-range-facet'; -import TaxonomyTermsFacet from './taxonomy-terms-facet'; - -/** - * Facet component. - * - * @param {object} props Props. - * @param {number} props.index Facet index. - * @param {string} props.name Facet name. - * @param {string} props.label Facet label. - * @param {string} props.postTypes Facet post types. - * @param {'post_type'|'price_range'|'taxonomy'} props.type Facet type. - * @returns {WPElement} Component element. - */ -export default ({ index, label, name, postTypes, type }) => { - const defaultIsOpen = index < 2; - - switch (type) { - case 'post_type': - return ; - case 'price_range': - return ; - case 'taxonomy': - return ( - - ); - default: - return null; - } -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/facets/post-type-facet.js b/lib/elasticpress/assets/js/instant-results/components/facets/post-type-facet.js deleted file mode 100644 index f64fea8..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/facets/post-type-facet.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useCallback, useContext, useMemo, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import { postTypeLabels } from '../../config'; -import Context from '../../context'; -import Panel from '../common/panel'; -import CheckboxList from '../common/checkbox-list'; -import { ActiveContraint } from '../tools/active-constraints'; - -/** - * Post type facet component. - * - * @param {object} props Props. - * @param {boolean} props.defaultIsOpen Whether the panel is open by default. - * @param {string} props.label Facet label. - * @returns {WPElement} Component element. - */ -export default ({ defaultIsOpen, label }) => { - const { - state: { - aggregations: { post_type: { post_type: { buckets = [] } = {} } = {} }, - args: { post_type: selectedPostTypes = [] }, - isLoading, - }, - dispatch, - } = useContext(Context); - - /** - * Create list of filter options from aggregation buckets. - * - * @param {Array} options List of options. - * @param {object} bucket Aggregation bucket. - * @param {string} bucket.key Aggregation key. - * @param {number} index Bucket index. - * @returns {Array} Array of options. - */ - const reduceOptions = useCallback( - (options, { doc_count, key }, index) => { - if (!Object.prototype.hasOwnProperty.call(postTypeLabels, key)) { - return options; - } - - options.push({ - checked: selectedPostTypes.includes(key), - count: doc_count, - id: `ep-search-post-type-${key}`, - label: postTypeLabels[key].singular, - order: index, - value: key, - }); - - return options; - }, - [selectedPostTypes], - ); - - /** - * Reduce buckets to options. - */ - const options = useMemo(() => buckets.reduce(reduceOptions, []), [buckets, reduceOptions]); - - /** - * Handle checkbox change event. - * - * @param {string[]} postTypes Selected post types. - */ - const onChange = (postTypes) => { - dispatch({ type: 'APPLY_ARGS', payload: { post_type: postTypes } }); - }; - - /** - * Handle clearing a post type. - * - * @param {string} postType Post type being cleared. - */ - const onClear = (postType) => { - const postTypes = [...selectedPostTypes]; - const index = postTypes.indexOf(postType); - - postTypes.splice(index, 1); - - dispatch({ type: 'APPLY_ARGS', payload: { post_type: postTypes } }); - }; - - return ( - options.length > 0 && ( - - {() => ( - <> - - - {selectedPostTypes.map((value) => ( - onClear(value)} - /> - ))} - - )} - - ) - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/facets/price-range-facet.js b/lib/elasticpress/assets/js/instant-results/components/facets/price-range-facet.js deleted file mode 100644 index 643fe79..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/facets/price-range-facet.js +++ /dev/null @@ -1,149 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useContext, useLayoutEffect, useState, WPElement } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import Context from '../../context'; -import { formatPrice } from '../../functions'; -import Panel from '../common/panel'; -import RangeSlider from '../common/range-slider'; -import { ActiveContraint } from '../tools/active-constraints'; - -/** - * Price range facet. - * - * @param {object} props Props. - * @param {boolean} props.defaultIsOpen Whether the panel is open by default. - * @param {string} props.label Facet label. - * @returns {WPElement} Component element. - */ -export default ({ defaultIsOpen, label }) => { - const { - state: { - aggregations: { - price_range: { - max_price: { value: maxAgg = null } = {}, - min_price: { value: minAgg = null } = {}, - } = {}, - }, - args: { max_price: maxArg = null, min_price: minArg = null }, - isLoading, - }, - dispatch, - } = useContext(Context); - - /** - * Minimum and maximum possible values. - */ - const max = Math.ceil(maxAgg); - const min = Math.floor(minAgg); - - /** - * Current minimum and maximum values. - */ - const [currentMaxValue, setCurrentMaxValue] = useState(0); - const [currentMinValue, setCurrentMinValue] = useState(0); - - /** - * Current minimum and maximum prices, formatted. - */ - const currentMaxPrice = formatPrice(currentMaxValue, { maximumFractionDigits: 0 }); - const currentMinPrice = formatPrice(currentMinValue, { maximumFractionDigits: 0 }); - - /** - * Applied minimum and maximum values. - */ - const maxValue = maxArg || max; - const minValue = minArg || min; - - /** - * Applied minimum and maximum prices, formatted. - */ - const maxPrice = formatPrice(maxValue, { maximumFractionDigits: 0 }); - const minPrice = formatPrice(minValue, { maximumFractionDigits: 0 }); - - /** - * Handle completed slider change. - * - * @param {number[]} values Lowest and highest values. - */ - const onAfterChange = (values) => { - const [min_price, max_price] = values; - - dispatch({ type: 'APPLY_ARGS', payload: { min_price, max_price } }); - }; - - /** - * Handle slider changes as they're made. - * - * @param {number[]} values Lowest and highest values. - */ - const onChange = ([min, max]) => { - setCurrentMinValue(min); - setCurrentMaxValue(max); - }; - - /** - * Handle clearing the filter. - */ - const onClear = () => { - dispatch({ type: 'APPLY_ARGS', payload: { max_price: null, min_price: null } }); - }; - - /** - * Effects. - */ - useLayoutEffect(() => { - const currentMaxValue = Math.min(max, maxValue); - const currentMinValue = Math.max(min, minValue); - - setCurrentMaxValue(currentMaxValue); - setCurrentMinValue(currentMinValue); - }, [min, max, minValue, maxValue]); - - return ( - maxAgg !== null && - minAgg !== null && ( - - {(isOpen) => ( - <> -
    -
    - {isOpen && ( - - )} -
    - -
    - {currentMinPrice} — {currentMaxPrice} -
    -
    - - {maxArg !== null && minArg !== null && ( - - )} - - )} -
    - ) - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/facets/search-term-facet.js b/lib/elasticpress/assets/js/instant-results/components/facets/search-term-facet.js deleted file mode 100644 index 637273c..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/facets/search-term-facet.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useContext, useEffect, useState, WPElement } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import Context from '../../context'; -import { useDebounce } from '../../hooks'; -import { ActiveContraint } from '../tools/active-constraints'; - -/** - * Search field component. - * - * @returns {WPElement} Component element. - */ -export default () => { - const { - state: { - args: { search }, - searchedTerm, - }, - dispatch, - } = useContext(Context); - - const [value, setValue] = useState(search); - - /** - * Dispatch the change, with debouncing. - */ - const dispatchChange = useDebounce((value) => { - dispatch({ type: 'NEW_SEARCH_TERM', payload: value }); - }, 300); - - /** - * Handle input changes. - * - * @param {Event} event Change event. - */ - const onChange = (event) => { - setValue(event.target.value); - dispatchChange(event.target.value); - }; - - /** - * Handle clearing. - */ - const onClear = () => { - dispatch({ type: 'NEW_SEARCH_TERM', payload: '' }); - }; - - /** - * Handle an external change to the search value, such as from popping - * state. - */ - const handleSearch = () => { - setValue(search); - }; - - /** - * Effects. - */ - useEffect(handleSearch, [search]); - - return ( - <> - - {searchedTerm && ( - - )} - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/facets/taxonomy-terms-facet.js b/lib/elasticpress/assets/js/instant-results/components/facets/taxonomy-terms-facet.js deleted file mode 100644 index eea74ba..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/facets/taxonomy-terms-facet.js +++ /dev/null @@ -1,162 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useCallback, useContext, useMemo, WPElement } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import { facets, postTypeLabels } from '../../config'; -import Context from '../../context'; -import Panel from '../common/panel'; -import CheckboxList from '../common/checkbox-list'; -import { ActiveContraint } from '../tools/active-constraints'; - -/** - * Taxonomy filter component. - * - * @param {object} props Components props. - * @param {boolean} props.defaultIsOpen Whether the panel is open by default. - * @param {string} props.label Facet label. - * @param {string} props.name Facet name. - * @param {Array} props.postTypes Facet post types. - * @returns {WPElement} Component element. - */ -export default ({ defaultIsOpen, label, postTypes, name }) => { - const { - state: { - isLoading, - args: { [name]: selectedTerms = [] }, - aggregations: { [name]: { [name]: { buckets = [] } = {} } = {} } = {}, - }, - dispatch, - } = useContext(Context); - - /** - * A unique label for the facet. Adds additional context to the label if - * another facet with the same label is being used. - */ - const uniqueLabel = useMemo(() => { - const isNotUnique = facets.some((facet) => facet.label === label && facet.name !== name); - const typeLabels = postTypes.map((postType) => postTypeLabels[postType].plural); - const typeSeparator = __(', ', 'elasticpress'); - - return isNotUnique - ? sprintf( - /* translators: %1$s: Facet label. $2$s: Facet post types. */ - __('%1$s (%2$s)', 'elasticpress'), - label, - typeLabels.join(typeSeparator), - ) - : label; - }, [label, postTypes, name]); - - /** - * Create list of filter options from aggregation buckets. - * - * @param {Array} options List of options. - * @param {object} bucket Aggregation bucket. - * @param {string} bucket.key Aggregation key. - * @returns {Array} Array of options. - */ - const reduceOptions = useCallback( - (options, { doc_count, key }) => { - const { name: label, parent, term_id, term_order } = JSON.parse(key); - - options.push({ - checked: selectedTerms.includes(term_id), - count: doc_count, - id: `ep-search-${name}-${term_id}`, - label, - parent: parent.toString(), - order: term_order, - value: term_id.toString(), - }); - - return options; - }, - [selectedTerms, name], - ); - - /** - * Reduce buckets to options. - */ - const options = useMemo(() => buckets.reduce(reduceOptions, []), [buckets, reduceOptions]); - - /** - * Reduce options to labels. - * - * @param {object} labels List of options. - * @param {object} bucket Aggregation bucket. - * @param {string} bucket.key Aggregation key. - * @returns {object} Options and their labels. - */ - const reduceLabels = useCallback((labels, { label, value }) => { - labels[value] = label; - - return labels; - }, []); - - /** - * Reduce buckets to labels. - */ - const labels = options.reduce(reduceLabels, {}); - - /** - * Handle checkbox change event. - * - * @param {string[]} terms Selected terms. - */ - const onChange = (terms) => { - dispatch({ type: 'APPLY_ARGS', payload: { [name]: terms } }); - }; - - /** - * Handle clearing a term. - * - * @param {string} term Term being cleared. - */ - const onClear = (term) => { - const terms = [...selectedTerms]; - - terms.splice(terms.indexOf(term), 1); - - dispatch({ type: 'APPLY_ARGS', payload: { [name]: terms } }); - }; - - return ( - options.length > 0 && ( - - {(isOpen) => ( - <> - {isOpen && ( - - )} - - {selectedTerms.map( - (value) => - labels?.[value] && ( - onClear(value)} - /> - ), - )} - - )} - - ) - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/layout.js b/lib/elasticpress/assets/js/instant-results/components/layout.js deleted file mode 100644 index a567cd6..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/layout.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useContext, WPElement } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import { facets } from '../config'; -import Context from '../context'; -import Facet from './facets/facet'; -import SearchTermFacet from './facets/search-term-facet'; -import Results from './layout/results'; -import Sidebar from './layout/sidebar'; -import Toolbar from './layout/toolbar'; -import ActiveConstraints from './tools/active-constraints'; -import ClearConstraints from './tools/clear-constraints'; -import SidebarToggle from './tools/sidebar-toggle'; -import Sort from './tools/sort'; - -/** - * Search dialog. - * - * @returns {WPElement} Component element. - */ -export default () => { - const { - state: { isLoading }, - } = useContext(Context); - - return ( -
    -
    - - - - - - - -
    - -
    - - - {facets.map(({ label, name, postTypes, type }, index) => ( - - ))} - - - -
    -
    - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/layout/results.js b/lib/elasticpress/assets/js/instant-results/components/layout/results.js deleted file mode 100644 index c28337e..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/layout/results.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Internal depenencies. - */ -import { useContext, useEffect, useRef, WPElement } from '@wordpress/element'; -import { _n, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import Context from '../../context'; -import Pagination from '../results/pagination'; -import Result from '../results/result'; -import Sort from '../tools/sort'; - -/** - * Search results component. - * - * @returns {WPElement} Component element. - */ -export default () => { - const { - state: { - args: { offset, per_page }, - searchResults, - searchedTerm, - totalResults, - }, - dispatch, - } = useContext(Context); - - const headingRef = useRef(); - - /** - * Handle clicking next. - */ - const onNext = () => { - dispatch({ type: 'NEXT_PAGE' }); - }; - - /** - * Handle clicking previous. - */ - const onPrevious = () => { - dispatch({ type: 'PREVIOUS_PAGE' }); - }; - - /** - * Effects. - */ - useEffect(() => { - headingRef.current.scrollIntoView({ behavior: 'smooth' }); - }, [offset]); - - return ( -
    -
    -

    - {searchedTerm - ? sprintf( - /* translators: %1$d: results count. %2$s: Search term. */ - _n( - '%1$d result for “%2$s“', - '%1$d results for “%2$s“', - totalResults, - 'elasticpress', - ), - totalResults, - searchedTerm, - ) - : sprintf( - /* translators: %d: results count. */ - _n('%d result', '%d results', totalResults, 'elasticpress'), - totalResults, - )} -

    - - -
    - - {searchResults.map((hit) => ( - - ))} - - -
    - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/layout/sidebar.js b/lib/elasticpress/assets/js/instant-results/components/layout/sidebar.js deleted file mode 100644 index 5c26ff1..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/layout/sidebar.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useContext, WPElement } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import Context from '../../context'; - -/** - * Search field component. - * - * @param {object} props Props. - * @param {WPElement} props.children Children. - * @returns {WPElement} Element. - */ -export default ({ children }) => { - const { - state: { isSidebarOpen }, - } = useContext(Context); - - return ( - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/layout/toolbar.js b/lib/elasticpress/assets/js/instant-results/components/layout/toolbar.js deleted file mode 100644 index dba407b..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/layout/toolbar.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * WordPress dependencies. - */ -import { WPElement } from '@wordpress/element'; - -/** - * Search field component. - * - * @param {object} props Props. - * @param {WPElement} props.children Children. - * @returns {WPElement} Element. - */ -export default ({ children }) => { - return
    {children}
    ; -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/results/pagination.js b/lib/elasticpress/assets/js/instant-results/components/results/pagination.js deleted file mode 100644 index 65d82d0..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/results/pagination.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __, sprintf } from '@wordpress/i18n'; -import { WPElement } from '@wordpress/element'; - -/** - * Search results component. - * - * @param {object} props Props. - * @param {number} props.offset Current items offset. - * @param {Function} props.onNext Next button handler. - * @param {Function} props.onPrevious Previous button handler. - * @param {number} props.perPage Items per page. - * @param {number} props.total Total number of items. - * @returns {WPElement} Element. - */ -export default ({ offset, onNext, onPrevious, perPage, total }) => { - /** - * Current page number. - */ - const currentPage = (offset + perPage) / perPage; - - /** - * Whether there are more pages. - */ - const nextIsAvailable = total > offset + perPage; - - /** - * Whether the are previous pages. - */ - const previousIsAvailable = offset > 0; - - /** - * Total pages. - */ - const totalPages = Math.ceil(total / perPage); - - return ( - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/results/result.js b/lib/elasticpress/assets/js/instant-results/components/results/result.js deleted file mode 100644 index 5d24652..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/results/result.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * WordPress dependencies. - */ -import { WPElement } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import { postTypeLabels, isWooCommerce } from '../../config'; -import StarRating from '../common/star-rating'; -import Image from '../common/image'; - -const { gmdateI18n } = wp.date; - -/** - * Search result. - * - * @param {object} props Component props. - * @param {object} props.hit Elasticsearch hit. - * @returns {WPElement} Component element. - */ -export default ({ hit }) => { - const { - highlight: { post_title: resultTitle, post_content_plain: resultContent = [] }, - _source: { - meta: { _wc_average_rating: [{ value: resultRating = 0 } = {}] = [] }, - post_date_gmt: resultDateGmt, - permalink: resultPermalink, - post_type: resultPostType, - price_html: priceHtml, - thumbnail: resultThumbnail = false, - }, - } = hit; - - const postTypeLabel = postTypeLabels[resultPostType]?.singular; - - return ( - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/tools/active-constraints.js b/lib/elasticpress/assets/js/instant-results/components/tools/active-constraints.js deleted file mode 100644 index f1b97f9..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/tools/active-constraints.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * WordPress dependencies. - */ -import { createSlotFill } from '@wordpress/components'; -import { WPElement } from '@wordpress/element'; -import { closeSmall, Icon } from '@wordpress/icons'; -import { __, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import SmallButton from '../common/small-button'; - -/** - * Create SlotFill. - */ -const { Fill, Slot } = createSlotFill('ActiveContraints'); - -/** - * Active filter component. - * - * @param {object} props Props. - * @param {string} props.label Constraint label. - * @param {Function} props.onClick Click handler. - * @returns {WPElement} Element. - */ -export const ActiveContraint = ({ label, onClick }) => { - return ( - - - - {label} - - - ); -}; - -/** - * Active constraints component. - * - * @returns {WPElement} Element. - */ -export default () => { - return {(fills) => fills}; -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/tools/clear-constraints.js b/lib/elasticpress/assets/js/instant-results/components/tools/clear-constraints.js deleted file mode 100644 index 97cd275..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/tools/clear-constraints.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useContext, useMemo, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import { facets } from '../../config'; -import Context from '../../context'; -import SmallButton from '../common/small-button'; - -/** - * Active constraints component. - * - * @returns {WPElement} Element. - */ -export default () => { - const { - state: { args }, - dispatch, - } = useContext(Context); - - /** - * Return whether there are active filters. - * - * Only filters that are available as facets are checked, as these are the - * only filters that will be cleared. This is to support applying filters - * that cannot be modified by the user. - * - * @returns {boolean} Whether there are active filters. - */ - const hasFilters = useMemo(() => { - return facets.some(({ name, type }) => { - switch (type) { - case 'post_type': - case 'taxonomy': - return args[name]?.length > 0; - case 'price_range': - return args.max_price || args.min_price; - default: - return args[name]; - } - }); - }, [args]); - - /** - * Handle clicking button. - * - * @returns {void} - */ - const onClick = () => { - dispatch({ type: 'CLEAR_FACETS' }); - }; - - return ( - hasFilters && ( - {__('Clear filters', 'elasticpress')} - ) - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/tools/sidebar-toggle.js b/lib/elasticpress/assets/js/instant-results/components/tools/sidebar-toggle.js deleted file mode 100644 index f3aa5e9..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/tools/sidebar-toggle.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * WordPress deendencies. - */ -import { useContext, WPElement } from '@wordpress/element'; -import { chevronDown, chevronUp, Icon } from '@wordpress/icons'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal deendencies. - */ -import Context from '../../context'; - -/** - * Open sidebar component. - * - * @returns {WPElement} Element. - */ -export default () => { - const { - state: { isSidebarOpen }, - dispatch, - } = useContext(Context); - - /** - * Handle click. - */ - const onClick = () => { - dispatch({ type: 'TOGGLE_SIDEBAR' }); - }; - - return ( - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/components/tools/sort.js b/lib/elasticpress/assets/js/instant-results/components/tools/sort.js deleted file mode 100644 index 865edc1..0000000 --- a/lib/elasticpress/assets/js/instant-results/components/tools/sort.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * WordPress deendencies. - */ -import { useContext, useMemo, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal deendencies. - */ -import { sortOptions } from '../../config'; -import Context from '../../context'; - -/** - * Search results component. - * - * @returns {WPElement} Component element. - */ -export default () => { - const { - state: { - args: { orderby, order }, - }, - dispatch, - } = useContext(Context); - - /** - * The key for the current sorting option. - */ - const currentOption = useMemo(() => { - return Object.keys(sortOptions).find((key) => { - return sortOptions[key].orderby === orderby && sortOptions[key].order === order; - }); - }, [orderby, order]); - - /** - * Handle sorting option change. - * - * @param {Event} event Change event. - */ - const onChange = (event) => { - const { orderby, order } = sortOptions[event.target.value]; - - dispatch({ type: 'APPLY_ARGS', payload: { orderby, order } }); - }; - - return ( - - ); -}; diff --git a/lib/elasticpress/assets/js/instant-results/config.js b/lib/elasticpress/assets/js/instant-results/config.js deleted file mode 100644 index 735fcef..0000000 --- a/lib/elasticpress/assets/js/instant-results/config.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -/** - * Window dependencies. - */ -const { - apiEndpoint, - apiHost, - argsSchema, - currencyCode, - facets, - isWooCommerce, - matchType, - paramPrefix, - postTypeLabels, - taxonomyLabels, -} = window.epInstantResults; - -/** - * Sorting options configuration. - */ -const sortOptions = { - relevance_desc: { - name: __('Most relevant', 'elasticpress'), - orderby: 'relevance', - order: 'desc', - currencyCode, - }, - date_desc: { - name: __('Date, newest to oldest', 'elasticpress'), - orderby: 'date', - order: 'desc', - }, - date_asc: { - name: __('Date, oldest to newest', 'elasticpress'), - orderby: 'date', - order: 'asc', - }, -}; - -/** - * Sort by price is only available for WooCommerce. - */ -if (isWooCommerce) { - sortOptions.price_desc = { - name: __('Price, highest to lowest', 'elasticpress'), - orderby: 'price', - order: 'desc', - }; - - sortOptions.price_asc = { - name: __('Price, lowest to highest', 'elasticpress'), - orderby: 'price', - order: 'asc', - }; -} - -export { - apiEndpoint, - apiHost, - argsSchema, - currencyCode, - facets, - isWooCommerce, - matchType, - paramPrefix, - postTypeLabels, - sortOptions, - taxonomyLabels, -}; diff --git a/lib/elasticpress/assets/js/instant-results/context.js b/lib/elasticpress/assets/js/instant-results/context.js deleted file mode 100644 index 05f9cf5..0000000 --- a/lib/elasticpress/assets/js/instant-results/context.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Internal dependencies. - */ -import { createContext } from '@wordpress/element'; - -export default createContext(); diff --git a/lib/elasticpress/assets/js/instant-results/functions.js b/lib/elasticpress/assets/js/instant-results/functions.js deleted file mode 100644 index 6b87893..0000000 --- a/lib/elasticpress/assets/js/instant-results/functions.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Internal deendencies. - */ -import { currencyCode, facets } from './config'; -import { sanitizeArg, sanitizeParam } from './utilities'; - -/** - * Clear facet filters from a set of args. - * - * @param {object} args Args to clear facets from. - * @returns {object} Cleared args. - */ -export const clearFacetsFromArgs = (args) => { - const clearedArgs = { ...args }; - - facets.forEach(({ name, type }) => { - switch (type) { - case 'price_range': - delete clearedArgs.max_price; - delete clearedArgs.min_price; - break; - default: - delete clearedArgs[name]; - break; - } - }); - - return clearedArgs; -}; - -/** - * Format a number as a price. - * - * @param {number} number Number to format. - * @param {object} options Formatter options. - * @returns {string} Formatted number. - */ -export const formatPrice = (number, options) => { - const format = new Intl.NumberFormat(navigator.language, { - style: 'currency', - currency: currencyCode, - currencyDisplay: 'narrowSymbol', - ...options, - }); - - return format.format(number); -}; - -/** - * Get the post types from a search form. - * - * @param {HTMLFormElement} form Form element. - * @returns {Array} Post types. - */ -export const getPostTypesFromForm = (form) => { - const data = new FormData(form); - - if (data.has('post_type')) { - return data.getAll('post_type').slice(-1); - } - - if (data.has('post_type[]')) { - return data.getAll('post_type[]'); - } - - return []; -}; - -/** - * Get permalink URL parameters from args. - * - * @typedef {object} ArgSchema - * @property {string} type Arg type. - * @property {any} [default] Default arg value. - * @property {Array} [allowedValues] Array of allowed values. - * - * @param {object} args Args - * @param {ArgSchema} schema Args schema. - * @param {string} [prefix] Prefix to prepend to args. - * @returns {URLSearchParams} URLSearchParams instance. - */ -export const getUrlParamsFromArgs = (args, schema, prefix = '') => { - const urlParams = new URLSearchParams(); - - Object.entries(schema).forEach(([arg, options]) => { - const param = prefix + arg; - const value = typeof args[arg] !== 'undefined' ? sanitizeParam(args[arg], options) : null; - - if (value !== null) { - urlParams.set(param, value); - } - }); - - return urlParams; -}; - -/** - * Build request args from URL parameters using a given schema. - * - * @typedef {object} ArgSchema - * @property {string} type Arg type. - * @property {any} [default] Default arg value. - * @property {Array} [allowedValues] Array of allowed values. - * - * @param {URLSearchParams} urlParams URL parameters. - * @param {object.} schema Schema to build args from. - * @param {string} [prefix] Parameter prefix. - * @param {boolean} [useDefaults] Whether to populate params with default values. - * @returns {object.} Query args. - */ -export const getArgsFromUrlParams = (urlParams, schema, prefix = '', useDefaults = true) => { - const args = Object.entries(schema).reduce((args, [arg, options]) => { - const param = urlParams.get(prefix + arg); - const value = - typeof param !== 'undefined' ? sanitizeArg(param, options, useDefaults) : null; - - if (value !== null) { - args[arg] = value; - } - - return args; - }, {}); - - return args; -}; diff --git a/lib/elasticpress/assets/js/instant-results/hooks.js b/lib/elasticpress/assets/js/instant-results/hooks.js deleted file mode 100644 index b1f16c6..0000000 --- a/lib/elasticpress/assets/js/instant-results/hooks.js +++ /dev/null @@ -1,70 +0,0 @@ -import { useCallback, useRef } from '@wordpress/element'; -import { apiEndpoint, apiHost } from './config'; - -/** - * Get debounced version of a function that only runs a given ammount of time - * after the last time it was run. - * - * @param {Function} callback Function to debounce. - * @param {number} delay Milliseconds to delay. - * @returns {Function} Debounced function. - */ -export const useDebounce = (callback, delay) => { - const timeout = useRef(null); - - return useCallback( - (...args) => { - window.clearTimeout(timeout.current); - - timeout.current = window.setTimeout(() => { - callback(...args); - }, delay); - }, - [callback, delay], - ); -}; - -/** - * Get a callback function for retrieving search results. - * - * @returns {Function} Memoized callback function for retrieving search results. - */ -export const useGetResults = () => { - const abort = useRef(new AbortController()); - const request = useRef(null); - - /** - * Get new search results from the API. - * - * @param {URLSearchParams} urlParams Query arguments. - * @returns {Promise} Request promise. - */ - const getResults = async (urlParams) => { - const url = `${apiHost}${apiEndpoint}?${urlParams.toString()}`; - - abort.current.abort(); - abort.current = new AbortController(); - - request.current = fetch(url, { - signal: abort.current.signal, - headers: { - Accept: 'application/json', - }, - }) - .then((response) => { - return response.json(); - }) - .catch((error) => { - if (error?.name !== 'AbortError' && !request.current) { - throw error; - } - }) - .finally(() => { - request.current = null; - }); - - return request.current; - }; - - return useCallback(getResults, []); -}; diff --git a/lib/elasticpress/assets/js/instant-results/index.js b/lib/elasticpress/assets/js/instant-results/index.js deleted file mode 100644 index 0cd3a08..0000000 --- a/lib/elasticpress/assets/js/instant-results/index.js +++ /dev/null @@ -1,242 +0,0 @@ -/** - * WordPress dependencies. - */ -import { SlotFillProvider } from '@wordpress/components'; -import { - render, - useCallback, - useEffect, - useMemo, - useReducer, - useRef, - WPElement, -} from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import { argsSchema, paramPrefix } from './config'; -import Context from './context'; -import { getArgsFromUrlParams, getUrlParamsFromArgs, getPostTypesFromForm } from './functions'; -import { useGetResults } from './hooks'; -import { reducer, initialState } from './reducer'; -import Layout from './components/layout'; -import Modal from './components/common/modal'; - -/** - * component. - * - * @returns {WPElement} Element. - */ -const App = () => { - const getResults = useGetResults(); - const [state, dispatch] = useReducer(reducer, initialState); - const inputRef = useRef(); - const modalRef = useRef(); - const stateRef = useRef(state); - - stateRef.current = state; - - /** - * Close the modal. - */ - const closeModal = useCallback(() => { - dispatch({ type: 'CLOSE_MODAL' }); - - if (inputRef.current) { - inputRef.current.focus(); - } - }, []); - - /** - * Start loading. - */ - const startLoading = useCallback(() => { - dispatch({ type: 'START_LOADING' }); - }, []); - - /** - * Finish loading. - */ - const finishLoading = useCallback(() => { - dispatch({ type: 'FINISH_LOADING' }); - }, []); - - /** - * Update search results. - * - * @param {object} response Search results. - */ - const updateResults = useCallback((response) => { - if (response) { - dispatch({ type: 'NEW_SEARCH_RESULTS', payload: response }); - } - }, []); - - /** - * Perform a search. - */ - const doSearch = useCallback(async () => { - const urlParams = getUrlParamsFromArgs(stateRef.current.args, argsSchema); - - startLoading(); - - const response = await getResults(urlParams); - - updateResults(response); - - finishLoading(); - }, [finishLoading, getResults, startLoading, updateResults]); - - /** - * Push state to history. - */ - const pushState = useCallback(() => { - const { history } = modalRef.current.ownerDocument.defaultView; - const { args, isOpen, isPoppingState } = stateRef.current; - - if (isPoppingState) { - return; - } - - const state = JSON.stringify({ ...args, isOpen }); - const params = getUrlParamsFromArgs(args, argsSchema, paramPrefix).toString(); - const url = isOpen ? `?${params}` : window.location.origin + window.location.pathname; - - if (history.state) { - history.pushState(state, document.title, url); - } else { - history.replaceState(state, document.title, window.location.href); - } - }, []); - - /** - * Handle escape key press. - * - * @param {Event} event Key down event. - */ - const onEscape = useCallback( - (event) => { - if (event.key === 'Escape') { - closeModal(); - } - }, - [closeModal], - ); - - /** - * Handle popstate event. - * - * @param {Event} event popstate event. - */ - const onPopState = useCallback((event) => { - if (event.state) { - dispatch({ type: 'POP_STATE', payload: JSON.parse(event.state) }); - } - }, []); - - /** - * Handle submitting the search form. - * - * @param {Event} event Input event. - */ - const onSubmit = useCallback((event) => { - event.preventDefault(); - - inputRef.current = event.target.s; - - const search = inputRef.current.value; - const post_type = getPostTypesFromForm(inputRef.current.form); - - dispatch({ type: 'APPLY_ARGS', payload: { search, post_type } }); - }, []); - - /** - * Handle changes to search parameters. - */ - const handleChanges = () => { - const { isOpen } = stateRef.current; - - pushState(); - - if (!isOpen) { - return; - } - - doSearch(); - }; - - /** - * Bind events to outside elements. - * - * @returns {Function} A cleanup function that unbinds the events. - */ - const handleEvents = () => { - const inputs = document.querySelectorAll('form input[type="search"'); - const modal = modalRef.current; - - inputs.forEach((input) => { - input.form.addEventListener('submit', onSubmit); - }); - - modal.ownerDocument.defaultView.addEventListener('popstate', onPopState); - - return () => { - inputs.forEach((input) => { - input.form.removeEventListener('submit', onSubmit); - }); - - modal.ownerDocument.defaultView.removeEventListener('popstate', onPopState); - }; - }; - - /** - * Open modal with pre-defined args if they are found in the URL. - */ - const handleInit = () => { - const urlParams = new URLSearchParams(window.location.search); - const args = getArgsFromUrlParams(urlParams, argsSchema, paramPrefix, false); - - if (Object.keys(args).length > 0) { - dispatch({ type: 'APPLY_ARGS', payload: args }); - } - }; - - /** - * Effects. - */ - useEffect(handleInit, []); - useEffect(handleEvents, [onEscape, onPopState, onSubmit]); - useEffect(handleChanges, [ - doSearch, - pushState, - state.args, - state.args.orderby, - state.args.order, - state.args.offset, - state.args.search, - ]); - - /** - * Create context. - */ - const context = useMemo(() => ({ state, dispatch }), [state, dispatch]); - - return ( - - - - - - - - ); -}; - -render(, document.getElementById('ep-instant-results')); diff --git a/lib/elasticpress/assets/js/instant-results/reducer.js b/lib/elasticpress/assets/js/instant-results/reducer.js deleted file mode 100644 index adfa333..0000000 --- a/lib/elasticpress/assets/js/instant-results/reducer.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Internal dependencies. - */ -import { matchType } from './config'; -import { clearFacetsFromArgs } from './functions'; - -/** - * Initial state. - */ -export const initialState = { - aggregations: {}, - args: { - highlight: '', - offset: 0, - orderby: 'relevance', - order: 'desc', - per_page: 6, - relation: matchType === 'all' ? 'and' : 'or', - search: '', - }, - isLoading: false, - isOpen: false, - isSidebarOpen: false, - isPoppingState: false, - searchResults: [], - searchedTerm: '', - totalResults: 0, -}; - -/** - * Reducer function for handling state changes. - * - * @param {object} state The current state. - * @param {object} action Action data. - * @param {string} action.type The action name. - * @param {object} action.payload New state data from the action. - * @returns {object} Updated state. - */ -export const reducer = (state, { type, payload }) => { - const newState = { ...state, isPoppingState: false }; - - switch (type) { - case 'APPLY_ARGS': { - newState.args = { ...newState.args, ...payload, offset: 0 }; - newState.isOpen = true; - break; - } - case 'CLEAR_FACETS': { - newState.args = clearFacetsFromArgs(newState.args); - break; - } - case 'NEW_SEARCH_TERM': { - newState.args = clearFacetsFromArgs(newState.args); - newState.args.offset = 0; - newState.args.search = payload; - - break; - } - case 'NEW_SEARCH_RESULTS': { - const { - hits: { hits, total }, - aggregations, - } = payload; - - /** - * Total number of items. - */ - const totalNumber = typeof total === 'number' ? total : total.value; - - newState.aggregations = aggregations; - newState.searchResults = hits; - newState.searchedTerm = newState.args.search; - newState.totalResults = totalNumber; - - break; - } - case 'NEXT_PAGE': { - newState.args.offset += newState.args.per_page; - break; - } - case 'PREVIOUS_PAGE': { - newState.args.offset = Math.max(newState.args.offset - newState.args.per_page, 0); - break; - } - case 'START_LOADING': { - newState.isLoading = true; - break; - } - case 'FINISH_LOADING': { - newState.isLoading = false; - break; - } - case 'TOGGLE_SIDEBAR': { - newState.isSidebarOpen = !state.isSidebarOpen; - break; - } - case 'CLOSE_MODAL': { - newState.args = clearFacetsFromArgs(newState.args); - newState.isOpen = false; - break; - } - case 'POP_STATE': { - const { isOpen, ...args } = payload; - - newState.args = args; - newState.isOpen = isOpen; - newState.isPoppingState = true; - - break; - } - default: - break; - } - - return newState; -}; diff --git a/lib/elasticpress/assets/js/instant-results/utilities.js b/lib/elasticpress/assets/js/instant-results/utilities.js deleted file mode 100644 index 7b60c83..0000000 --- a/lib/elasticpress/assets/js/instant-results/utilities.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Sanitize an argument value based on its type. - * - * @param {*} value The value. - * @param {object} options Sanitization options. - * @param {'number'|'numbers'|'string'|'strings'} options.type (optional) Value type. - * @param {Array} options.allowedValues (optional) Allowed values. - * @param {*} options.default (optional) Default value. - * @param {boolean} [useDefaults] Whether to return default values. - * @returns {*} Sanitized value. - */ -export const sanitizeArg = (value, options, useDefaults = true) => { - let sanitizedValue = null; - - switch (value && options.type) { - case 'number': - sanitizedValue = parseFloat(value, 10) || null; - break; - case 'numbers': - sanitizedValue = decodeURIComponent(value) - .split(',') - .map((v) => parseFloat(v, 10)) - .filter(Boolean); - break; - case 'string': - sanitizedValue = value.toString(); - break; - case 'strings': - sanitizedValue = decodeURIComponent(value) - .split(',') - .map((v) => v.toString().trim()); - break; - default: - break; - } - - /** - * If there is a list of allowed values, make sure the value is - * allowed. - */ - if (options.allowedValues) { - sanitizedValue = options.allowedValues.includes(sanitizedValue) ? sanitizedValue : null; - } - - /** - * Populate a default value if one is available and we still don't - * have a value. - */ - if (useDefaults && sanitizedValue === null && typeof options.default !== 'undefined') { - sanitizedValue = options.default; - } - - return sanitizedValue; -}; - -/** - * Sanitize a parameter value based on its type. - * - * @param {*} value The value. - * @param {object} options Sanitization options. - * @param {'number'|'numbers'|'string'|'strings'} options.type (optional) Value type. - * @param {Array} options.allowedValues (optional) Allowed values. - * @param {*} options.default (optional) Default value. - * @param {boolean} [useDefaults] Whether to return default values. - * @returns {*} Sanitized value. - */ -export const sanitizeParam = (value, options, useDefaults = true) => { - let sanitizedValue = null; - - switch (value && options.type) { - case 'number': - case 'string': - sanitizedValue = value; - break; - case 'numbers': - case 'strings': - sanitizedValue = value.join(','); - break; - default: - break; - } - - /** - * If there is a list of allowed values, make sure the value is - * allowed. - */ - if (options.allowedValues) { - sanitizedValue = options.allowedValues.includes(sanitizedValue) ? sanitizedValue : null; - } - - /** - * Populate a default value if one is available and we still don't - * have a value. - */ - if (useDefaults && sanitizedValue === null && typeof options.default !== 'undefined') { - sanitizedValue = options.default; - } - - return sanitizedValue; -}; diff --git a/lib/elasticpress/assets/js/notice.js b/lib/elasticpress/assets/js/notice.js deleted file mode 100644 index 035d9c5..0000000 --- a/lib/elasticpress/assets/js/notice.js +++ /dev/null @@ -1,21 +0,0 @@ -import jQuery from 'jquery'; - -const { epAdmin, ajaxurl } = window; - -jQuery('.notice').on('click', '.notice-dismiss', (event) => { - const notice = event.delegateTarget.getAttribute('data-ep-notice'); - - if (!notice) { - return; - } - - jQuery.ajax({ - method: 'post', - data: { - nonce: epAdmin.nonce, - action: 'ep_notice_dismiss', - notice, - }, - url: ajaxurl, - }); -}); diff --git a/lib/elasticpress/assets/js/ordering/index.js b/lib/elasticpress/assets/js/ordering/index.js deleted file mode 100644 index 37312cc..0000000 --- a/lib/elasticpress/assets/js/ordering/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import ReactDOM from 'react-dom'; - -import { Pointers } from './pointers'; - -ReactDOM.render(, document.getElementById('ordering-app')); diff --git a/lib/elasticpress/assets/js/ordering/pointers.js b/lib/elasticpress/assets/js/ordering/pointers.js deleted file mode 100644 index 13f4c0f..0000000 --- a/lib/elasticpress/assets/js/ordering/pointers.js +++ /dev/null @@ -1,503 +0,0 @@ -// External -import React, { Component } from 'react'; -import apiFetch from '@wordpress/api-fetch'; -import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; -import { __ } from '@wordpress/i18n'; -import { pluck, debounce } from '../utils/helpers'; - -apiFetch.use(apiFetch.createRootURLMiddleware(window.epOrdering.restApiRoot)); - -export class Pointers extends Component { - titleInput = null; - - debouncedDefaultResults = debounce(() => { - this.getDefaultResults(); - }, 200); - - doSearch = debounce(() => { - const { searchText, searchResults } = this.state; - const searchTerm = searchText; - - // Set loading state - searchResults[searchTerm] = false; - this.setState({ searchResults }); - - apiFetch({ - path: `/elasticpress/v1/pointer_search?s=${searchTerm}`, - }).then((result) => { - searchResults[searchTerm] = result; - - this.setState({ searchResults }); - }); - }, 200); - - debouncedHandleTitleChange = debounce(() => { - this.handleTitleChange(); - }, 200); - - /** - * Initializes the component with initial state set by WP - * - * @param {object} props Component props - */ - constructor(props) { - super(props); - - // We need to know the title of the page and react to changes since this is the query we search for - this.titleInput = document.getElementById('title'); - - this.state = { - pointers: window.epOrdering.pointers, - posts: window.epOrdering.posts, - title: this.titleInput.value, - defaultResults: {}, - searchText: '', - searchResults: {}, - }; - } - - componentDidMount() { - this.titleInput.addEventListener('keyup', this.debouncedHandleTitleChange); - - const { title } = this.state; - - if (title?.length > 0) { - this.getDefaultResults(); - } - } - - componentWillUnmount() { - this.titleInput.removeEventListener('keyup', this.debouncedHandleTitleChange); - } - - handleTitleChange = () => { - this.setState({ title: this.titleInput.value }); - this.debouncedDefaultResults(); - }; - - getDefaultResults = () => { - const { title: searchTerm } = this.state; - - apiFetch({ - path: `/elasticpress/v1/pointer_preview?s=${searchTerm}`, - }).then((result) => { - const { defaultResults } = this.state; - - defaultResults[searchTerm] = result; - - this.setState({ defaultResults }); - }); - }; - - removePointer = (pointer) => { - let { pointers } = this.state; - - delete pointers[pointers.indexOf(pointer)]; - pointers = pointers.filter((item) => item !== null); - - this.setState({ pointers }); - }; - - getMergedPosts = () => { - let { pointers } = this.state; - const { title, defaultResults } = this.state; - let merged = defaultResults[title].slice(); - - const setIds = {}; - merged.forEach((item) => { - setIds[item.ID] = item; - }); - - pointers = pointers.sort((a, b) => { - return a.order > b.order ? 1 : -1; - }); - - pointers.forEach((pointer) => { - // Remove the original if a duplicate - if (setIds[pointer.ID]) { - delete merged[merged.indexOf(setIds[pointer.ID])]; - merged = merged.filter((item) => item); - } - - // Insert into proper location - merged.splice(parseInt(pointer.order, 10) - 1, 0, pointer); - }); - - return merged; - }; - - /** - * Gets the next available position for a pointer - * - * @returns {number|false} The available position - */ - getNextAvailablePosition = () => { - const { pointers } = this.state; - const availablePositions = {}; - - for (let i = 1; i <= window.epOrdering.postsPerPage; i++) { - availablePositions[i] = true; - } - - pointers.forEach((item) => { - delete availablePositions[item.order]; - }); - - const keys = Object.keys(availablePositions); - - if (keys.length === 0) { - return false; - } - - return parseInt(keys[0], 10); - }; - - /** - * Adds a new pointer. We place the new pointer at the highest available position - * - * @param {object} post Post object - */ - addPointer = (post) => { - const id = post.ID; - const { posts, pointers } = this.state; - - if (!posts[id]) { - posts[id] = post; - this.setState({ posts }); - } - - const position = this.getNextAvailablePosition(); - - if (!position) { - /* eslint-disable no-alert */ - window.alert( - __('You have added the maximum number of custom results.', 'elasticpress'), - ); - /* eslint-enable no-alert */ - return; - } - - pointers.push({ - ID: id, - order: position, - }); - - this.setState({ pointers }); - }; - - /** - * Callback when drag/drop is complete. - * - * Only the pointers are able to be dragged around, so all we need to do is increase any pointer by one that is - * either at the current position or greater - * - * @param {object} result Dragged object - */ - onDragComplete = (result) => { - // dropped outside the list - if (!result.destination) { - return; - } - - const items = this.getMergedPosts(); - - // Offsetting indexes when over posts per page to account for the non-sortable notice - const ppp = parseInt(window.epOrdering.postsPerPage, 10); - const startIndex = - result.source.index >= ppp ? result.source.index - 1 : result.source.index; - const endIndex = - result.destination.index > ppp - ? result.destination.index - 1 - : result.destination.index; - - const [removed] = items.splice(startIndex, 1); - items.splice(endIndex, 0, removed); - - // Now _all_ the items are in order - grab the pointers and set the new positions to state - const pointers = []; - - items.forEach((item, index) => { - if (item.order) { - // Reordering an existing pointer - pointers.push({ - ID: item.ID, - order: index + 1, - }); - } else if (item.ID === result.draggableId) { - // Adding a default post to the pointers array - pointers.push({ - ID: item.ID, - order: index + 1, - }); - } - }); - - this.setState({ pointers }); - }; - - searchResults = (searchResults) => { - const { searchText } = this.state; - - if (searchText === '') { - return null; - } - - if (searchResults === false) { - return ( -
    -
    - Loading... -
    - ); - } - - if (searchResults.length === 0) { - return
    {__('No results found.', 'elasticpress')}
    ; - } - - return searchResults.map((result) => { - return ( -
    - {result.post_title} - { - event.preventDefault(); - this.addPointer(result); - }} - onKeyDown={(event) => { - event.preventDefault(); - this.addPointer(result); - }} - > - {__('Add Post', 'elasticpress')} - -
    - ); - }); - }; - - /** - * Renders the component - * - * @returns {*} The component - */ - render() { - const { - posts, - defaultResults, - title, - pointers, - searchText, - searchResults: searchResultsFromState, - } = this.state; - - if (title.length === 0) { - return ( -
    -

    - {__( - 'Enter your search query above to preview the results.', - 'elasticpress', - )} -

    -
    - ); - } - - if (!defaultResults[title]) { - return ( -
    -
    - {__('Loading Result Preview…', 'elasticpress')} -
    - ); - } - - // We need to reference these by ID later - const defaultResultsById = {}; - defaultResults[title].forEach((item) => { - defaultResultsById[item.ID] = item; - }); - - const mergedPosts = this.getMergedPosts(); - const renderedIds = pluck(pointers, 'ID'); - - const searchResults = searchResultsFromState[searchText] - ? searchResultsFromState[searchText].filter( - (item) => renderedIds.indexOf(item.ID) === -1, - ) - : false; - - return ( -
    - - - - - {(provided) => ( -
    - {mergedPosts.map((item, index) => { - const draggableIndex = - parseInt(window.epOrdering.postsPerPage, 10) <= index - ? index + 1 - : index; - - let { title } = item; - if (undefined === title) { - title = - undefined !== posts[item.ID] - ? posts[item.ID].post_title - : defaultResultsById[item.ID].post_title; - } - - // Determine if this result is part of default search results or not - const isDefaultResult = - undefined !== defaultResultsById[item.ID]; - const tooltipText = - isDefaultResult === true - ? __('Return to original position', 'elasticpress') - : __( - 'Remove custom result from results list', - 'elasticpress', - ); - - return ( - - {parseInt(window.epOrdering.postsPerPage, 10) === - index && ( - - {(component) => ( -
    - - {__( - 'The following posts have been displaced to the next page of search results.', - 'elasticpress', - )} - -
    - )} -
    - )} - - - {(provided2) => ( -
    - {item.order && isDefaultResult === true && ( - RD - )} - {item.order && - isDefaultResult === false && ( - - CR - - )} - {title} -
    - - {item.order && ( - { - event.preventDefault(); - this.removePointer(item); - }} - onKeyDown={(event) => { - event.preventDefault(); - this.removePointer(item); - }} - > - - Remove Post - - - )} -
    -
    - )} -
    -
    - ); - })} - {provided.placeholder} -
    - )} -
    -
    - -
    -
    - CR - - {__('Custom Result (manually added to list)', 'elasticpress')} - -
    -
    - RD - - {__( - 'Reordered Default (originally in results, but repositioned)', - 'elasticpress', - )} - -
    -
    - -
    -

    {__('Add to results', 'elasticpress')}

    - -
    -
    - { - this.setState({ searchText: e.target.value }); - this.doSearch(); - }} - /> -
    - -
    {this.searchResults(searchResults)}
    -
    -
    -
    - ); - } -} diff --git a/lib/elasticpress/assets/js/settings.js b/lib/elasticpress/assets/js/settings.js deleted file mode 100644 index 5fd3591..0000000 --- a/lib/elasticpress/assets/js/settings.js +++ /dev/null @@ -1,46 +0,0 @@ -import jQuery from 'jquery'; - -const $epCredentialsTab = jQuery(document.getElementsByClassName('ep-credentials-tab')); -const $epCredentialsHostLabel = jQuery('.ep-host-row label'); -const $epCredentialsHostLegend = jQuery(document.getElementsByClassName('ep-host-legend')); -const $epCredentialsAdditionalFields = jQuery( - document.getElementsByClassName('ep-additional-fields'), -); -const epHostField = document.getElementById('ep_host'); -const epHost = epHostField ? epHostField.value : null; -let epHostNewValue = ''; - -if (epHostField) { - epHostField.addEventListener('input', (e) => { - epHostNewValue = e.target.value; - }); -} - -$epCredentialsTab.on('click', (e) => { - const epio = e.currentTarget.getAttribute('data-epio') !== null; - const $target = jQuery(e.currentTarget); - const initial = $target.hasClass('initial'); - - e.preventDefault(); - - if (initial && !epHostField.disabled) { - epHostField.value = epHost; - } else { - epHostField.value = epHostNewValue; - } - - $epCredentialsTab.removeClass('nav-tab-active'); - $target.addClass('nav-tab-active'); - - if (epio) { - $epCredentialsHostLabel.text('ElasticPress.io Host URL'); - $epCredentialsHostLegend.text('Plug in your ElasticPress.io server here!'); - $epCredentialsAdditionalFields.show(); - $epCredentialsAdditionalFields.attr('aria-hidden', 'false'); - } else { - $epCredentialsHostLabel.text('Elasticsearch Host URL'); - $epCredentialsHostLegend.text('Plug in your Elasticsearch server here!'); - $epCredentialsAdditionalFields.hide(); - $epCredentialsAdditionalFields.attr('aria-hidden', 'true'); - } -}); diff --git a/lib/elasticpress/assets/js/sites-admin.js b/lib/elasticpress/assets/js/sites-admin.js deleted file mode 100644 index e97da24..0000000 --- a/lib/elasticpress/assets/js/sites-admin.js +++ /dev/null @@ -1,28 +0,0 @@ -import jQuery from 'jquery'; - -const { epsa } = window; - -window.addEventListener('load', function () { - const toggles = document.getElementsByClassName('index-toggle'); - for (let i = 0; i < toggles.length; i++) { - toggles[i].addEventListener('click', function () { - const checked = this.checked ? 'yes' : 'no'; - - jQuery.get( - epsa.ajax_url, - { - action: 'ep_site_admin', - blog_id: this.dataset.blogid, - nonce: epsa.nonce, - checked, - }, - () => { - document.getElementById(`switch-label-${this.dataset.blogid}`).innerHTML = this - .checked - ? 'On' - : 'Off'; - }, - ); - }); - } -}); diff --git a/lib/elasticpress/assets/js/stats.js b/lib/elasticpress/assets/js/stats.js deleted file mode 100644 index 9d27046..0000000 --- a/lib/elasticpress/assets/js/stats.js +++ /dev/null @@ -1,145 +0,0 @@ -/* eslint-disable no-new */ - -import Chart from 'chart.js'; - -const { epChartData } = window; - -/** - * Generates a random string representing a color. - * - * @returns {string} Random color - */ -function getRandomColor() { - const letters = '0123456789ABCDEF'; - let color = '#'; - - for (let i = 0; i < 6; i += 1) { - color += letters[Math.floor(Math.random() * 16)]; - } - - return color; -} - -const barData = Object.entries(epChartData.indices_data); -const barLabels = []; -const barDocs = []; -const barColors = []; - -Chart.defaults.global.legend.labels.usePointStyle = true; - -barData.forEach(function (data) { - barLabels.push(data[1].name); - barDocs.push(data[1].docs); - barColors.push(getRandomColor()); -}); - -const documentChart = document.getElementById('documentChart'); -if (documentChart) { - new Chart(documentChart, { - type: 'horizontalBar', - data: { - labels: barLabels, - datasets: [ - { - label: 'Documents', - backgroundColor: barColors, - data: barDocs, - }, - ], - }, - options: { - legend: { - display: false, - }, - title: { - display: true, - }, - }, - }); -} - -const queriesTotalChart = document.getElementById('queriesTotalChart'); -if (queriesTotalChart) { - new Chart(document.getElementById('queriesTotalChart'), { - type: 'pie', - data: { - labels: ['Indexing operations', 'Total Query operations'], - datasets: [ - { - label: '', - backgroundColor: ['#5ba9a7', '#2e7875', '#a980a4'], - data: [epChartData.index_total, epChartData.query_total], - }, - ], - }, - options: { - responsive: false, - title: { - display: true, - }, - legend: { - position: 'right', - }, - tooltips: { - callbacks: { - /** - * Appends the string operations before tooltip value - * - * @param {object} item Chat item - * @param {object} data Data - * @returns {string} Operations - */ - label(item, data) { - const dataset = data.datasets[item.datasetIndex]; - const currentValue = dataset.data[item.index]; - - return `Operations: ${currentValue}`; - }, - }, - }, - }, - }); -} - -const queriesTimeChart = document.getElementById('queriesTimeChart'); -if (queriesTimeChart) { - new Chart(queriesTimeChart, { - type: 'pie', - data: { - labels: ['Avg indexing time in ms', 'Avg query time in ms'], - datasets: [ - { - label: '', - backgroundColor: ['#9ea6c7', '#93b3d5'], - data: [epChartData.index_time_in_millis, epChartData.query_time_in_millis], - }, - ], - }, - options: { - responsive: false, - title: { - display: true, - }, - legend: { - position: 'right', - }, - tooltips: { - callbacks: { - /** - * Appends the string milliseconds after tooltip value - * - * @param {object} item Tooltip item - * @param {object} data Tooltip data - * @returns {string} current value in milliseconds - */ - label(item, data) { - const dataset = data.datasets[item.datasetIndex]; - const currentValue = dataset.data[item.index]; - - return `${+currentValue} milliseconds`; - }, - }, - }, - }, - }); -} diff --git a/lib/elasticpress/assets/js/sync.js b/lib/elasticpress/assets/js/sync.js deleted file mode 100644 index a0389af..0000000 --- a/lib/elasticpress/assets/js/sync.js +++ /dev/null @@ -1,829 +0,0 @@ -import apiFetch from '@wordpress/api-fetch'; -import { dateI18n } from '@wordpress/date'; - -/* eslint-disable camelcase, no-use-before-define */ -const { epDash, history } = window; -const { __, sprintf } = wp.i18n; - -const { ajax_url: ajaxurl = '', is_epio } = epDash; - -// Main elements of sync page -const syncBox = document.querySelector('.ep-sync-data'); -const deleteAndSyncBox = document.querySelector('.ep-delete-data-and-sync'); - -// It could be the syncBox or deleteAndSyncBox -let activeBox; - -// Buttons to start a sync or delete data -const syncButton = syncBox.querySelector('.ep-sync-box__button-sync'); -const deleteAndSyncButton = deleteAndSyncBox.querySelector( - '.ep-delete-data-and-sync__button-delete', -); - -// Log elements -const syncBoxFulllogTab = document.querySelector('.ep-sync-data .ep-sync-box__output-tab-fulllog'); -const syncBoxOutputFulllog = document.querySelector('.ep-sync-data .ep-sync-box__output-fulllog'); -const syncBoxErrorTab = document.querySelector('.ep-sync-data .ep-sync-box__output-tab-error'); -const syncBoxOutputError = document.querySelector('.ep-sync-data .ep-sync-box__output-error'); - -const deleteBoxFulllogTab = document.querySelector( - '.ep-delete-data-and-sync .ep-sync-box__output-tab-fulllog', -); -const deleteBoxErrorTab = document.querySelector( - '.ep-delete-data-and-sync .ep-sync-box__output-tab-error', -); -const deleteBoxOutputFulllog = document.querySelector( - '.ep-delete-data-and-sync .ep-sync-box__output-fulllog', -); -const deleteBoxOutputError = document.querySelector( - '.ep-delete-data-and-sync .ep-sync-box__output-error', -); - -syncButton.addEventListener('click', function () { - activeBox = syncBox; - - disableButtonsInDeleteBox(); - - syncButton.style.display = 'none'; - updateDisabledAttribute(syncButton, true); - - const learnMoreLink = activeBox.querySelector('.ep-sync-box__learn-more-link'); - learnMoreLink.style.display = 'none'; - - showPauseStopButtons(); - showProgress(); - addLineToOutput(__('Indexing data…', 'elasticpress')); - - const progressInfoElement = activeBox.querySelector('.ep-sync-box__progress-info'); - const progressBar = activeBox.querySelector('.ep-sync-box__progressbar_animated'); - const startDateTime = activeBox.querySelector('.ep-sync-box__start-time-date'); - - progressInfoElement.innerText = __('Sync in progress', 'elasticpress'); - - progressBar.style.width = `0`; - progressBar.innerText = ``; - - startDateTime.innerText = ''; - - startSyncProcess(); -}); - -deleteAndSyncButton.addEventListener('click', deleteAndSync); - -function deleteAndSync() { - activeBox = deleteAndSyncBox; - - disableButtonsInSyncBox(); - updateDisabledAttribute(deleteAndSyncButton, true); - showPauseStopButtons(); - showProgress(); - - addLineToOutput(__('Deleting data…', 'elasticpress')); - - const progressInfoElement = activeBox.querySelector('.ep-sync-box__progress-info'); - const progressBar = activeBox.querySelector('.ep-sync-box__progressbar_animated'); - const startDateTime = activeBox.querySelector('.ep-sync-box__start-time-date'); - - progressInfoElement.innerText = __('Deleting in progress', 'elasticpress'); - - progressBar.style.width = `0`; - progressBar.innerText = ``; - - startDateTime.innerText = ''; - - startSyncProcess(true); -} - -/** - * Show Pause and Stop buttons on the active box - */ -function showPauseStopButtons() { - if (activeBox) { - showStopButton(); - showPauseButton(); - } -} - -/** - * Hide Pause and Stop buttons on the active box - */ -function hidePauseStopButtons() { - hideStopButton(); - hidePauseButton(); -} - -/** - * Show Pause button on the active box - */ -function showPauseButton() { - if (activeBox) { - const pauseButton = activeBox.querySelector('.ep-sync-box__button-pause'); - - updateDisabledAttribute(pauseButton, false); - - pauseButton.style.display = 'flex'; - } -} - -/** - * Hide Pause button on the active box - */ -function hidePauseButton() { - if (activeBox) { - const pauseButton = activeBox.querySelector('.ep-sync-box__button-pause'); - - pauseButton.style.display = 'none'; - } -} - -/** - * Show Resume button on the active box - */ -function showResumeButton() { - if (activeBox) { - const resumeButton = activeBox.querySelector('.ep-sync-box__button-resume'); - - updateDisabledAttribute(resumeButton, false); - - resumeButton.style.display = 'flex'; - } -} - -/** - * Hide Pause button on the active box - */ -function hideResumeButton() { - if (activeBox) { - const resumeButton = activeBox.querySelector('.ep-sync-box__button-resume'); - - resumeButton.style.display = 'none'; - } -} - -/** - * Show Stop button on the active box - */ -function showStopButton() { - if (activeBox) { - const stopButton = activeBox.querySelector('.ep-sync-box__button-stop'); - - updateDisabledAttribute(stopButton, false); - - stopButton.style.display = 'flex'; - } -} - -/** - * Hide Stop button on the active box - */ -function hideStopButton() { - if (activeBox) { - const stopButton = activeBox.querySelector('.ep-sync-box__button-stop'); - - stopButton.style.display = 'none'; - } -} - -function showProgress() { - const progressWrapper = activeBox?.querySelector('.ep-sync-box__progress-wrapper'); - - if (progressWrapper?.style) { - progressWrapper.style.display = 'block'; - } -} - -let syncStatus = 'sync'; -let syncStack; -let processed = 0; -let toProcess = 0; -let totalProcessed = 0; - -updateLastSyncDateTime(epDash?.ep_last_sync_date); - -if (epDash.index_meta) { - if (epDash.index_meta.method === 'cli') { - syncStatus = 'wpcli'; - processed = epDash?.index_meta?.items_indexed; - toProcess = epDash?.index_meta?.total_items; - - activeBox = epDash.index_meta.put_mapping ? deleteAndSyncBox : syncBox; - - const progressInfoElement = activeBox.querySelector('.ep-sync-box__progress-info'); - - progressInfoElement.innerText = __('WP-CLI sync in progress', 'elasticpress'); - - updateStartDateTime(epDash?.index_meta?.start_date_time); - - updateDisabledAttribute(syncButton, true); - updateDisabledAttribute(deleteAndSyncButton, true); - - showProgress(); - - updateSyncDash(); - cliSync(); - } else { - processed = epDash.index_meta.offset; - toProcess = epDash.index_meta.found_items; - - if (epDash.index_meta.sync_stack) { - syncStack = epDash.index_meta.sync_stack; - } - - if ((!syncStack || !syncStack.length) && toProcess === 0 && !epDash.index_meta.start) { - // Sync finished - syncStatus = 'finished'; - } else { - syncStatus = 'pause'; - } - activeBox = epDash.index_meta?.put_mapping ? deleteAndSyncBox : syncBox; - - disableButtonsInSyncBox(); - disableButtonsInDeleteBox(); - - if (activeBox === syncBox) { - syncButton.style.display = 'none'; - - const learnMoreLink = activeBox.querySelector('.ep-sync-box__learn-more-link'); - - learnMoreLink.style.display = 'none'; - } - - showResumeButton(); - showStopButton(); - - showProgress(); - - updateSyncDash(); - } -} else if (epDash.auto_start_index) { - deleteAndSync(); - - history.pushState( - {}, - document.title, - document.location.pathname + document.location.search.replace(/&do_sync/, ''), - ); -} - -/** - * Change the disabled attribute of an element - * - * @param {HTMLElement} element Element to be updated - * @param {boolean} value The value used in disabled attribute - */ -function updateDisabledAttribute(element, value) { - element.disabled = value; -} - -/** - * Update dashboard with syncing information - */ -function updateSyncDash() { - const progressBar = activeBox.querySelector('.ep-sync-box__progressbar_animated'); - - const isSyncing = ['initialsync', 'sync', 'pause', 'wpcli'].includes(syncStatus); - - let progressBarWidth; - if (isSyncing) { - progressBarWidth = - toProcess === 0 ? 0 : (parseInt(processed, 10) / parseInt(toProcess, 10)) * 100; - } else { - progressBarWidth = 100; - } - - if ( - typeof progressBarWidth === 'number' && - !Number.isNaN(progressBarWidth) && - Number.isFinite(progressBarWidth) - ) { - const width = Math.min(100, progressBarWidth); - progressBar.style.width = `${width}%`; - progressBar.innerText = `${Math.trunc(width)}%`; - } - - if (isSyncing) { - progressBar.classList.remove('ep-sync-box__progressbar_complete'); - } else if (syncStatus === 'interrupt') { - const progressInfoElement = activeBox.querySelector('.ep-sync-box__progress-info'); - - progressInfoElement.innerText = __('Sync interrupted', 'elasticpress'); - - updateDisabledAttribute(deleteAndSyncButton, false); - updateDisabledAttribute(syncButton, false); - - hidePauseStopButtons(); - hideResumeButton(); - - syncButton.style.display = 'flex'; - - const learnMoreLink = activeBox.querySelector('.ep-sync-box__learn-more-link'); - - if (learnMoreLink?.style) { - learnMoreLink.style.display = 'block'; - } - } else { - const progressInfoElement = activeBox.querySelector('.ep-sync-box__progress-info'); - - progressInfoElement.innerText = __('Sync completed', 'elasticpress'); - - progressBar.classList.add('ep-sync-box__progressbar_complete'); - - updateDisabledAttribute(deleteAndSyncButton, false); - updateDisabledAttribute(syncButton, false); - - hidePauseStopButtons(); - hideResumeButton(); - - syncButton.style.display = 'flex'; - - const learnMoreLink = activeBox.querySelector('.ep-sync-box__learn-more-link'); - - if (learnMoreLink?.style) { - learnMoreLink.style.display = 'block'; - } - } -} - -/** - * Cancel a sync - */ -function cancelSync() { - toProcess = 0; - processed = 0; - totalProcessed = 0; - - apiFetch({ - url: ajaxurl, - method: 'POST', - body: new URLSearchParams({ - action: 'ep_cancel_index', - nonce: epDash.nonce, - }), - }); -} - -function cliSync() { - const requestSettings = { - url: ajaxurl, - method: 'POST', - body: new URLSearchParams({ - action: 'ep_cli_index', - nonce: epDash.nonce, - }), - }; - - apiFetch(requestSettings).then((response) => { - if (syncStatus === 'interrupt') { - return; - } - - if (syncStatus === 'wpcli') { - toProcess = response.data?.index_meta?.total_items; - processed = response.data?.index_meta?.items_indexed; - - if (response.data.index_meta?.current_sync_item?.failed) { - const message = response.data?.message; - if (Array.isArray(message)) { - message.forEach((item) => { - addErrorToOutput(item); - addLineToOutput(item); - }); - } else if (typeof message === 'string') { - addErrorToOutput(message); - addLineToOutput(message); - } - } else { - addLineToOutput(response.data.message); - } - - updateSyncDash(); - - if (response.data?.index_meta?.indexing) { - cliSync(); - return; - } - } - - syncStatus = 'finished'; - addLineToOutput('==============================='); - addLineToOutput(__('WP-CLI sync is finished', 'elasticpress')); - updateSyncDash(); - }); -} - -/** - * Add a line to the active output - * - * @param {string} text Message to show on output - */ -function addLineToOutput(text) { - if (activeBox && text) { - const wrapperElement = activeBox.querySelector('.ep-sync-box__output-wrapper'); - - const lastLineNumberElement = activeBox.querySelector( - '.ep-sync-box__output-line:last-child .ep-sync-box__output-line-number', - ); - const lastLineNumber = Number(lastLineNumberElement?.innerText); - - const lineNumber = document.createElement('div'); - lineNumber.className = 'ep-sync-box__output-line-number'; - lineNumber.innerText = - typeof lastLineNumber === 'number' && !Number.isNaN(lastLineNumber) - ? lastLineNumber + 1 - : 1; - - const lineText = document.createElement('div'); - lineText.className = 'ep-sync-box__output-line-text'; - lineText.innerText = text; - - const line = document.createElement('div'); - line.className = 'ep-sync-box__output-line'; - line.append(lineNumber); - line.append(lineText); - - wrapperElement.append(line); - - const outputElement = activeBox.querySelector('.ep-sync-box__output_active'); - outputElement.scrollTo(0, wrapperElement.scrollHeight); - } -} - -function addErrorToOutput(text) { - if (activeBox) { - const wrapperElement = activeBox.querySelector( - '.ep-sync-box__output-error .ep-sync-box__output-wrapper', - ); - - const lastLineNumberElement = activeBox.querySelector( - '.ep-sync-box__output-error .ep-sync-box__output-line:last-child .ep-sync-box__output-line-number', - ); - const lastLineNumber = Number(lastLineNumberElement?.innerText); - - const lineNumber = document.createElement('div'); - lineNumber.className = 'ep-sync-box__output-line-number'; - lineNumber.innerText = - typeof lastLineNumber === 'number' && !Number.isNaN(lastLineNumber) - ? lastLineNumber + 1 - : 1; - - const lineText = document.createElement('div'); - lineText.className = 'ep-sync-box__output-line-text'; - lineText.innerText = text; - - const line = document.createElement('div'); - line.className = 'ep-sync-box__output-line'; - line.append(lineNumber); - line.append(lineText); - - wrapperElement.append(line); - - const errorTab = activeBox.querySelector('.ep-sync-box__output-tab-error'); - - errorTab.innerText = sprintf( - // translators: Number of errors - __('Errors (%d)', 'elasticpress'), - lineNumber.innerText, - ); - - const outputElement = activeBox.querySelector('.ep-sync-box__output-error'); - outputElement.scrollTo(0, wrapperElement.scrollHeight); - } -} - -/** - * Update the start datetime on active box - * - * @param {Date | string} dateValue The datetime value - */ -function updateStartDateTime(dateValue) { - if (dateValue) { - const startDateTime = activeBox.querySelector('.ep-sync-box__start-time-date'); - - if (startDateTime) { - startDateTime.innerText = dateI18n( - // translators: index start date format, see https://wordpress.org/support/article/formatting-date-and-time/ - __('D, F d, Y H:i', 'elasticpress'), - dateValue, - ); - } - } -} - -/** - * Update the last sync datetime - * - * @param {Date | string} dateValue Date object or string, parsable by moment.js. - */ -function updateLastSyncDateTime(dateValue) { - if (dateValue) { - const lastSyncDate = document.querySelector('.ep-last-sync__date'); - - if (lastSyncDate) { - lastSyncDate.innerText = dateI18n( - // translators: last sync datetime format, see https://wordpress.org/support/article/formatting-date-and-time/ - __('D, F d, Y H:i', 'elasticpress'), - dateValue, - ); - } - } -} - -/** - * Check if a destructive index is running - * - * @returns {boolean} Wheter or not is a destructive index - */ -function isDestructiveIndex() { - return activeBox === deleteAndSyncBox; -} - -/** - * Interrupt the sync process - * - * @param {boolean} value True to interrupt the sync process - */ -function shouldInterruptSync(value) { - if (!value) { - return; - } - - syncStatus = 'interrupt'; - - let logMessage = __('Sync interrupted by WP-CLI command', 'elasticpress'); - if (isDestructiveIndex()) { - logMessage = sprintf( - // translators: ElasticPress.io or Elasticsearch - __( - 'Your indexing process has been stopped by WP-CLI and your %s index could be missing content. To restart indexing, please click the Start button or use WP-CLI commands to perform the reindex. Please note that search results could be incorrect or incomplete until the reindex finishes.', - 'elasticpress', - ), - is_epio ? 'ElasticPress.io' : 'Elasticsearch', - ); - } - stopIndex(__('Sync interrupted', 'elasticpress'), logMessage); -} - -/** - * Perform an elasticpress sync - * - * @param {boolean} putMapping Whetever mapping should be sent or not. - */ -function sync(putMapping = false) { - const requestSettings = { - url: ajaxurl, - method: 'POST', - body: new URLSearchParams({ - action: 'ep_index', - put_mapping: putMapping ? 1 : 0, - nonce: epDash.nonce, - }), - }; - - apiFetch(requestSettings) - .then((response) => { - if (response.data.index_meta?.current_sync_item?.failed) { - const message = response.data?.message; - if (Array.isArray(message)) { - message.forEach((item) => { - addErrorToOutput(item); - addLineToOutput(item); - }); - } else if (typeof message === 'string') { - addErrorToOutput(message); - addLineToOutput(message); - } - } else { - addLineToOutput(response.data.message); - } - updateStartDateTime(response?.data?.index_meta?.start_date_time); - shouldInterruptSync(response.data?.index_meta?.should_interrupt_sync); - - if (response.data?.method === 'cli') { - syncStatus = 'wpcli'; - cliSync(); - return; - } - - if (syncStatus !== 'sync') { - return; - } - - if (!response.data.index_meta) { - syncStatus = 'finished'; - - const lastSyncStatusIcon = document.querySelector('.ep-last-sync__icon-status'); - const lastSyncStatus = document.querySelector('.ep-last-sync__status'); - - lastSyncStatusIcon.src = response.data.totals.failed - ? lastSyncStatusIcon.src?.replace(/thumbsup/, 'thumbsdown') - : lastSyncStatusIcon.src?.replace(/thumbsdown/, 'thumbsup'); - lastSyncStatus.innerText = response.data.totals.failed - ? __('Sync unsuccessful on ', 'elasticpress') - : __('Sync success on ', 'elasticpress'); - - updateLastSyncDateTime(response.data?.totals?.end_date_time); - - updateSyncDash(); - - addLineToOutput('==============================='); - - if (epDash.install_sync) { - document.location.replace(epDash.install_complete_url); - } - - activeBox = undefined; - - processed = 0; - toProcess = 0; - totalProcessed = 0; - - return; - } - - if (!toProcess) { - toProcess = response.data?.index_meta?.current_sync_item?.found_items; - - if (response.data?.index_meta?.sync_stack) { - syncStack = response.data.index_meta.sync_stack; - - toProcess = syncStack?.reduce((previousValue, currentSync) => { - return previousValue + currentSync.found_items; - }, toProcess); - } - } - - if (response.data.index_meta.offset === 0 && processed > 0) { - totalProcessed = processed; - } - - processed = totalProcessed + response.data.index_meta.offset; - - updateSyncDash(); - sync(putMapping); - }) - .catch((response) => { - if ( - response && - response.status && - parseInt(response.status, 10) >= 400 && - parseInt(response.status, 10) < 600 - ) { - syncStatus = 'error'; - updateSyncDash(); - - cancelSync(); - } - - updateDisabledAttribute(syncButton, false); - updateDisabledAttribute(deleteAndSyncButton, false); - }); -} - -/** - * Start sync process - * - * @param {boolean} putMapping Determines whether to send the mapping and delete all data before sync. - */ -function startSyncProcess(putMapping) { - syncStatus = 'initialsync'; - - const progressWrapperElement = activeBox.querySelector('.ep-sync-box__progress-wrapper'); - const progressInfoElement = activeBox.querySelector('.ep-sync-box__progress-info'); - const progressBar = activeBox.querySelector('.ep-sync-box__progressbar_animated'); - const startDateTime = activeBox.querySelector('.ep-sync-box__start-time-date'); - - progressWrapperElement.style.display = 'block'; - progressInfoElement.innerText = __('Sync in progress', 'elasticpress'); - - progressBar.style.width = `0`; - progressBar.innerText = ``; - - startDateTime.innerText = ''; - - updateSyncDash(); - - syncStatus = 'sync'; - - sync(putMapping); -} - -/** - * Disable buttons in the Sync box - */ -function disableButtonsInSyncBox() { - const buttons = syncBox.querySelectorAll('.ep-sync-data button'); - - buttons.forEach((button) => updateDisabledAttribute(button, true)); -} - -/** - * Disable buttons in the Delete box - */ -function disableButtonsInDeleteBox() { - const buttons = deleteAndSyncBox.querySelectorAll('.ep-delete-data-and-sync button'); - - buttons.forEach((button) => updateDisabledAttribute(button, true)); -} - -document.querySelectorAll('.ep-sync-box__button-pause')?.forEach((button) => { - button?.addEventListener('click', function () { - syncStatus = 'pause'; - - const progressInfoElement = activeBox?.querySelector('.ep-sync-box__progress-info'); - - if (progressInfoElement?.innerText) { - progressInfoElement.innerText = __('Sync paused', 'elasticpress'); - } - - updateSyncDash(); - - hidePauseButton(); - showResumeButton(); - - addLineToOutput(__('Sync paused', 'elasticpress')); - }); -}); - -document.querySelectorAll('.ep-sync-box__button-resume')?.forEach((button) => { - button?.addEventListener('click', function () { - syncStatus = 'sync'; - - const progressInfoElement = activeBox.querySelector('.ep-sync-box__progress-info'); - - progressInfoElement.innerText = __('Sync in progress', 'elasticpress'); - - updateSyncDash(); - - hideResumeButton(); - showPauseButton(); - - sync(); - }); -}); - -function stopIndex(syncMessage, logMessage) { - syncStatus = syncStatus === 'wpcli' ? 'interrupt' : 'cancel'; - - const progressInfoElement = activeBox.querySelector('.ep-sync-box__progress-info'); - const progressBar = activeBox.querySelector('.ep-sync-box__progressbar_animated'); - - updateSyncDash(); - - cancelSync(); - - progressInfoElement.innerText = syncMessage; - - progressBar.style.width = `0`; - progressBar.innerText = ``; - - addLineToOutput(logMessage); -} -document.querySelectorAll('.ep-sync-box__button-stop')?.forEach((button) => { - button?.addEventListener('click', () => { - stopIndex(__('Sync stopped', 'elasticpress'), __('Sync stopped', 'elasticpress')); - }); -}); - -document.querySelectorAll('.ep-sync-box__show-hide-log')?.forEach((element) => { - element.addEventListener('click', function (event) { - event.preventDefault(); - - if (element.nextElementSibling?.classList?.toggle('ep-sync-box__output-tabs_hide')) { - element.innerText = __('Show log', 'elasticpress'); - } else { - element.innerText = __('Hide log', 'elasticpress'); - } - }); -}); - -syncBoxFulllogTab.addEventListener('click', function () { - syncBoxFulllogTab.classList.add('ep-sync-box__output-tab_active'); - syncBoxOutputFulllog.classList.add('ep-sync-box__output_active'); - - syncBoxErrorTab.classList.remove('ep-sync-box__output-tab_active'); - syncBoxOutputError.classList.remove('ep-sync-box__output_active'); -}); - -syncBoxErrorTab.addEventListener('click', function () { - syncBoxErrorTab.classList.add('ep-sync-box__output-tab_active'); - syncBoxOutputError.classList.add('ep-sync-box__output_active'); - - syncBoxFulllogTab.classList.remove('ep-sync-box__output-tab_active'); - syncBoxOutputFulllog.classList.remove('ep-sync-box__output_active'); -}); - -deleteBoxFulllogTab.addEventListener('click', function () { - deleteBoxFulllogTab.classList.add('ep-sync-box__output-tab_active'); - deleteBoxOutputFulllog.classList.add('ep-sync-box__output_active'); - - deleteBoxErrorTab.classList.remove('ep-sync-box__output-tab_active'); - deleteBoxOutputError.classList.remove('ep-sync-box__output_active'); -}); - -deleteBoxErrorTab.addEventListener('click', function () { - deleteBoxErrorTab.classList.add('ep-sync-box__output-tab_active'); - deleteBoxOutputError.classList.add('ep-sync-box__output_active'); - - deleteBoxFulllogTab.classList.remove('ep-sync-box__output-tab_active'); - deleteBoxOutputFulllog.classList.remove('ep-sync-box__output_active'); -}); diff --git a/lib/elasticpress/assets/js/synonyms/components/SynonymsEditor.js b/lib/elasticpress/assets/js/synonyms/components/SynonymsEditor.js deleted file mode 100644 index 4e706fb..0000000 --- a/lib/elasticpress/assets/js/synonyms/components/SynonymsEditor.js +++ /dev/null @@ -1,120 +0,0 @@ -import React, { useContext, useEffect } from 'react'; -import { State, Dispatch } from '../context'; -import SetsEditor from './editors/SetsEditor'; -import AlterativesEditor from './editors/AlternativesEditor'; -import SolrEditor from './editors/SolrEditor'; - -/** - * Synonyms editor component. - * - * @returns {React.FC} Synonyms component - */ -const SynonymsEditor = () => { - const state = useContext(State); - const dispatch = useContext(Dispatch); - const { alternatives, sets, isSolrEditable, isSolrVisible, dirty, submit } = state; - const { - pageHeading, - pageDescription, - pageToggleAdvanceText, - pageToggleSimpleText, - alternativesTitle, - alternativesDescription, - setsTitle, - setsDescription, - solrTitle, - solrDescription, - submitText, - } = window.epSynonyms.i18n; - - /** - * Checks if the form is valid. - * - * @param {object} _state Current state. - * @returns {boolean} If the form is valid - */ - const isValid = (_state) => { - return [..._state.sets, ..._state.alternatives].reduce((valid, item) => { - return !valid ? valid : item.valid; - }, true); - }; - - /** - * Handles submitting the form. - */ - const handleSubmit = () => { - if (isSolrEditable) { - dispatch({ type: 'REDUCE_SOLR_TO_STATE' }); - } - - dispatch({ type: 'VALIDATE_ALL' }); - dispatch({ type: 'REDUCE_STATE_TO_SOLR' }); - dispatch({ type: 'SUBMIT' }); - }; - - /** - * Handle toggling the editor type. - */ - const handleToggleAdvance = () => { - if (isSolrEditable) { - dispatch({ type: 'REDUCE_SOLR_TO_STATE' }); - } else { - dispatch({ type: 'REDUCE_STATE_TO_SOLR' }); - } - - dispatch({ type: 'SET_SOLR_EDITABLE', data: !isSolrEditable }); - }; - - useEffect(() => { - if (submit && !dirty && isValid(state)) { - document.querySelector('.wrap form').submit(); - } - }, [submit, dirty, state]); - - return ( - <> -

    - {pageHeading}{' '} - -

    -

    {pageDescription}

    - - {!isSolrEditable && ( - <> -
    -

    {`${setsTitle} (${sets.length})`}

    -

    {setsDescription}

    - -
    -
    -

    {`${alternativesTitle} (${alternatives.length})`}

    -

    {alternativesDescription}

    - -
    - - )} - -
    - {isSolrVisible &&

    {solrTitle}

    } - {isSolrVisible &&

    {solrDescription}

    } - -
    - - - -
    - -
    - - ); -}; - -export default SynonymsEditor; diff --git a/lib/elasticpress/assets/js/synonyms/components/editors/AlternativeEditor.js b/lib/elasticpress/assets/js/synonyms/components/editors/AlternativeEditor.js deleted file mode 100644 index 5cbf934..0000000 --- a/lib/elasticpress/assets/js/synonyms/components/editors/AlternativeEditor.js +++ /dev/null @@ -1,81 +0,0 @@ -import React, { useState, useEffect, useContext, useRef } from 'react'; -import LinkedMultiInput from '../shared/LinkedMultiInput'; -import { Dispatch } from '../../context'; - -/** - * Alternative Editor - * - * @param {object} props Props. - * @returns {React.FC} AlternativeEditor component - */ -const AlternativeEditor = (props) => { - const { id, synonyms, removeAction, updateAction } = props; - const primary = synonyms.find((item) => item.primary); - const [primaryTerm, setPrimaryTerm] = useState(primary ? primary.value : ''); - const dispatch = useContext(Dispatch); - const primaryRef = useRef(null); - - /** - * Create primary token - * - * @param {string} label Label. - * @returns {object} Primary token - */ - const createPrimaryToken = (label) => { - return { - label, - value: label, - primary: true, - }; - }; - - /** - * Handle key down. - * - * @param {React.SyntheticEvent} event Keydown event. - */ - const handleKeyDown = (event) => { - switch (event.key) { - case 'Enter': - event.preventDefault(); - break; - default: - } - }; - - useEffect(() => { - dispatch({ - type: 'UPDATE_ALTERNATIVE_PRIMARY', - data: { id, token: createPrimaryToken(primaryTerm) }, - }); - }, [primaryTerm, id, dispatch]); - - useEffect(() => { - primaryRef.current.focus(); - }, [primaryRef]); - - const memoizedSynonyms = React.useMemo(() => { - return synonyms.filter((item) => !item.primary); - }, [synonyms]); - - return ( - <> - setPrimaryTerm(e.target.value)} - value={primaryTerm} - onKeyDown={handleKeyDown} - ref={primaryRef} - /> - - - ); -}; - -export default AlternativeEditor; diff --git a/lib/elasticpress/assets/js/synonyms/components/editors/AlternativesEditor.js b/lib/elasticpress/assets/js/synonyms/components/editors/AlternativesEditor.js deleted file mode 100644 index 2d211ff..0000000 --- a/lib/elasticpress/assets/js/synonyms/components/editors/AlternativesEditor.js +++ /dev/null @@ -1,70 +0,0 @@ -import React, { Fragment, useContext } from 'react'; -import AlternativeEditor from './AlternativeEditor'; -import { Dispatch, State } from '../../context'; - -/** - * Synonyms editor component. - * - * @param {object} props Props. - * @param {object[]} props.alternatives Defined alternatives (explicit mappings). - * @returns {React.FC} AlternativesEditor component - */ -const AlternativesEditor = ({ alternatives }) => { - const dispatch = useContext(Dispatch); - const state = useContext(State); - const { - alternativesInputHeading, - alternativesPrimaryHeading, - alternativesAddButtonText, - alternativesErrorMessage, - } = window.epSynonyms.i18n; - - /** - * Handle click. - * - * @param {React.SyntheticEvent} e Event. - */ - const handleClick = (e) => { - const [lastItem] = state.alternatives.slice(-1); - if (!alternatives.length || lastItem.synonyms.filter(({ value }) => value.length).length) { - dispatch({ type: 'ADD_ALTERNATIVE' }); - } - e.preventDefault(); - }; - - return ( -
    -
    -

    - - {alternativesPrimaryHeading} - - - {alternativesInputHeading} - -

    -
    - {alternatives.map((props) => ( - -
    - -
    - {!props.valid && ( -

    {alternativesErrorMessage}

    - )} -
    - ))} - -
    -
    -
    - ); -}; - -export default AlternativesEditor; diff --git a/lib/elasticpress/assets/js/synonyms/components/editors/SetsEditor.js b/lib/elasticpress/assets/js/synonyms/components/editors/SetsEditor.js deleted file mode 100644 index 557f94b..0000000 --- a/lib/elasticpress/assets/js/synonyms/components/editors/SetsEditor.js +++ /dev/null @@ -1,60 +0,0 @@ -import React, { useContext, Fragment } from 'react'; -import LinkedMultiInput from '../shared/LinkedMultiInput'; -import { Dispatch, State } from '../../context'; - -/** - * Synonyms editor component. - * - * @param {object} props Props - * @param {object[]} props.sets Defined sets (equivalent synonyms). - * @returns {React.FC} SetsEditor component - */ -const SetsEditor = ({ sets }) => { - const dispatch = useContext(Dispatch); - const state = useContext(State); - const { setsInputHeading, setsAddButtonText, setsErrorMessage } = window.epSynonyms.i18n; - - /** - * Handle click. - * - * @param {React.SyntheticEvent} e Event - */ - const handleClick = (e) => { - const [lastSet] = state.sets.slice(-1); - if (!sets.length || lastSet.synonyms.length) { - dispatch({ type: 'ADD_SET' }); - } - e.preventDefault(); - }; - - return ( -
    -
    -

    - {setsInputHeading} -

    -
    - {sets.map((props) => ( - -
    - -
    - {!props.valid && ( -

    {setsErrorMessage}

    - )} -
    - ))} - -
    -
    -
    - ); -}; - -export default SetsEditor; diff --git a/lib/elasticpress/assets/js/synonyms/components/editors/SolrEditor.js b/lib/elasticpress/assets/js/synonyms/components/editors/SolrEditor.js deleted file mode 100644 index a960e0e..0000000 --- a/lib/elasticpress/assets/js/synonyms/components/editors/SolrEditor.js +++ /dev/null @@ -1,54 +0,0 @@ -import React, { useContext } from 'react'; -import { State, Dispatch } from '../../context'; - -/** - * Synonym Inspector - * - * @returns {React.FC} SolrEditor Component - */ -const SolrEditor = () => { - const state = useContext(State); - const dispatch = useContext(Dispatch); - const { alternatives, isSolrEditable, isSolrVisible, sets, solr } = state; - const { - synonymsTextareaInputName, - solrInputHeading, - solrAlternativesErrorMessage, - solrSetsErrorMessage, - } = window.epSynonyms.i18n; - - return ( -
    -
    -

    - {solrInputHeading} -

    -
    -