Semi working conditions for the new ui
This commit is contained in:
@@ -47,6 +47,8 @@ class Partnerexpo_Core_Public {
|
||||
public function searchbox_shortcode() {
|
||||
wp_enqueue_style( $this->plugin_name . '-searchbox-css' );
|
||||
wp_enqueue_script( $this->plugin_name . '-searchbox-js' );
|
||||
wp_enqueue_style( $this->plugin_name . '-multiselect-css' );
|
||||
wp_enqueue_script( $this->plugin_name . '-multiselect-js' );
|
||||
|
||||
ob_start();
|
||||
include plugin_dir_path( __FILE__ ) . 'partials/partnerexpo-core-public-searchbox.php';
|
||||
@@ -62,12 +64,44 @@ class Partnerexpo_Core_Public {
|
||||
}
|
||||
|
||||
function query_partners(WP_REST_Request $request) {
|
||||
$params = $request->get_query_params();
|
||||
|
||||
if (isset($params['force_tags']) && isset($params['tags']) && $params['force_tags'] === 'true') {
|
||||
$params['tags'] = str_replace(',', '+', $params['tags']);
|
||||
}
|
||||
|
||||
$args = [
|
||||
'post_type' => 'pexpo_partners',
|
||||
'posts_per_page' => 15,
|
||||
'posts_per_page' => $params['resultsPerPage'] ?? 10,
|
||||
'paged' => $params['page'] ?? 1,
|
||||
'post_status' => 'publish',
|
||||
's' => $params['q'] ?? '',
|
||||
'pexpo_tags' => $params['tags'] ?? '',
|
||||
];
|
||||
|
||||
switch ($params['sort'] ?? 'relevance') {
|
||||
case 'date_asc':
|
||||
$args['orderby'] = 'date';
|
||||
$args['order'] = 'ASC';
|
||||
break;
|
||||
case 'date_desc':
|
||||
$args['orderby'] = 'date';
|
||||
$args['order'] = 'DESC';
|
||||
break;
|
||||
case 'title_asc':
|
||||
$args['orderby'] = 'title';
|
||||
$args['order'] = 'ASC';
|
||||
break;
|
||||
case 'title_desc':
|
||||
$args['orderby'] = 'title';
|
||||
$args['order'] = 'DESC';
|
||||
break;
|
||||
case 'relevance':
|
||||
default:
|
||||
// Default WordPress search sorting
|
||||
break;
|
||||
}
|
||||
|
||||
$query = new WP_Query($args);
|
||||
|
||||
$posts = [];
|
||||
@@ -83,6 +117,7 @@ class Partnerexpo_Core_Public {
|
||||
'date' => date('Y-m-d', strtotime($post->post_date)),
|
||||
'tag' => $tags ?? null,
|
||||
'url' => get_permalink($post),
|
||||
'order' => $params['sort'] ?? 'relevance',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -96,7 +131,7 @@ class Partnerexpo_Core_Public {
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
public function register_styles() {
|
||||
wp_register_style(
|
||||
$this->plugin_name . '-searchbox-css',
|
||||
plugin_dir_url( __FILE__ ) . 'css/searchbox.css',
|
||||
@@ -104,6 +139,14 @@ class Partnerexpo_Core_Public {
|
||||
$this->version,
|
||||
'all'
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
$this->plugin_name . '-multiselect-css',
|
||||
plugin_dir_url( __FILE__ ) . 'css/multiselect.css',
|
||||
[],
|
||||
$this->version,
|
||||
'all'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +154,7 @@ class Partnerexpo_Core_Public {
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
public function register_scripts() {
|
||||
wp_register_script(
|
||||
$this->plugin_name . '-searchbox-js',
|
||||
plugin_dir_url( __FILE__ ) . 'js/searchbox.js',
|
||||
@@ -119,6 +162,14 @@ class Partnerexpo_Core_Public {
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
$this->plugin_name . '-multiselect-js',
|
||||
plugin_dir_url( __FILE__ ) . 'js/multiselect.js',
|
||||
[],
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
206
public/css/multiselect.css
Normal file
206
public/css/multiselect.css
Normal file
@@ -0,0 +1,206 @@
|
||||
:root {
|
||||
--spacing-smaller: 3px;
|
||||
--spacing-small: 5px;
|
||||
--spacing-medium: 7px;
|
||||
--spacing-large: 12px;
|
||||
--font-size: 12px;
|
||||
--font-size-large: 14px;
|
||||
--font-size-larger: 16px;
|
||||
--line-height: 16px;
|
||||
--line-height-larger: 20px;
|
||||
--primary-color: #40c979;
|
||||
--text-color-dark: #212529;
|
||||
--text-color: #585858;
|
||||
--text-color-light: #1018289e;
|
||||
--border-color: #bebebe;
|
||||
--border-color-light: #f1f3f5;
|
||||
--input-placeholder: #65727e;
|
||||
--input-background: #e9e9ed;
|
||||
--input-border: #dee2e6;
|
||||
--input-border-active: #c1c9d0;
|
||||
--input-border-invalid: #e44e4e;
|
||||
--input-outline-invalid: rgba(219, 138, 138, 0.5);
|
||||
--input-color: #e9e9ed;
|
||||
--input-disabled: #f7f7f7;
|
||||
--input-min-height: 45px;
|
||||
--options-height: 40dvh;
|
||||
--option-background: #f3f4f7;
|
||||
--border-radius: 10px;
|
||||
--icon-size: 12px;
|
||||
--icon-space: 30px;
|
||||
--checkbox-size: 16px;
|
||||
--checkbox-radius: 4px;
|
||||
--checkbox-border: #ced4da;
|
||||
--checkbox-background: #fff;
|
||||
--checkbox-active: #fff;
|
||||
--checkbox-thickness: 2px;
|
||||
}
|
||||
.multi-select {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
.multi-select .multi-select-header {
|
||||
border: 1px solid var(--input-border);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacing-medium) var(--spacing-large);
|
||||
padding-right: var(--icon-space);
|
||||
overflow: hidden;
|
||||
gap: var(--spacing-medium);
|
||||
min-height: var(--input-min-height);
|
||||
}
|
||||
.multi-select .multi-select-header::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
transform: translateY(-50%);
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23949ba3' viewBox='0 0 16 16'%3E%3Cpath d='M8 13.1l-8-8 2.1-2.2 5.9 5.9 5.9-5.9 2.1 2.2z'/%3E%3C/svg%3E");
|
||||
height: var(--icon-size);
|
||||
width: var(--icon-size);
|
||||
}
|
||||
.multi-select .multi-select-header.multi-select-header-active {
|
||||
border-color: var(--input-border-active);
|
||||
}
|
||||
.multi-select .multi-select-header.multi-select-header-active::after {
|
||||
transform: translateY(-50%) rotate(180deg);
|
||||
}
|
||||
.multi-select .multi-select-header.multi-select-header-active + .multi-select-options {
|
||||
display: flex;
|
||||
}
|
||||
.multi-select .multi-select-header .multi-select-header-placeholder {
|
||||
color: var(--text-color-light);
|
||||
}
|
||||
.multi-select .multi-select-header .multi-select-header-option {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background-color: var(--option-background);
|
||||
font-size: var(--font-size-large);
|
||||
padding: var(--spacing-smaller) var(--spacing-small);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
.multi-select .multi-select-header .multi-select-header-max {
|
||||
font-size: var(--font-size-large);
|
||||
color: var(--text-color-light);
|
||||
}
|
||||
.multi-select .multi-select-options {
|
||||
display: none;
|
||||
box-sizing: border-box;
|
||||
flex-flow: wrap;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
margin-top: var(--spacing-small);
|
||||
padding: var(--spacing-small);
|
||||
background-color: #fff;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
max-height: var(--options-height);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.multi-select .multi-select-options::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
.multi-select .multi-select-options::-webkit-scrollbar-track {
|
||||
background: #f0f1f3;
|
||||
}
|
||||
.multi-select .multi-select-options::-webkit-scrollbar-thumb {
|
||||
background: #cdcfd1;
|
||||
}
|
||||
.multi-select .multi-select-options::-webkit-scrollbar-thumb:hover {
|
||||
background: #b2b6b9;
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-option,
|
||||
.multi-select .multi-select-options .multi-select-all {
|
||||
padding: var(--spacing-large);
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-option .multi-select-option-radio,
|
||||
.multi-select .multi-select-options .multi-select-all .multi-select-option-radio {
|
||||
background: var(--checkbox-background);
|
||||
margin-right: var(--spacing-large);
|
||||
height: var(--checkbox-size);
|
||||
width: var(--checkbox-size);
|
||||
border: 1px solid var(--checkbox-border);
|
||||
border-radius: var(--checkbox-radius);
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-option .multi-select-option-text,
|
||||
.multi-select .multi-select-options .multi-select-all .multi-select-option-text {
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: inherit;
|
||||
font-size: var(--font-size-larger);
|
||||
line-height: var(--line-height);
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-radio,
|
||||
.multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-radio {
|
||||
border-color: var(--primary-color);
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-radio::after,
|
||||
.multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-radio::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: calc(var(--checkbox-size) / 4);
|
||||
height: calc(var(--checkbox-size) / 2);
|
||||
border: solid var(--checkbox-active);
|
||||
border-width: 0 var(--checkbox-thickness) var(--checkbox-thickness) 0;
|
||||
transform: rotate(45deg) translate(50%, -25%);
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-text,
|
||||
.multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-text {
|
||||
color: var(--text-color-dark);
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-option:hover, .multi-select .multi-select-options .multi-select-option:active,
|
||||
.multi-select .multi-select-options .multi-select-all:hover, .multi-select .multi-select-options .multi-select-all:active {
|
||||
background-color: var(--option-background);
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-all {
|
||||
border-bottom: 1px solid var(--border-color-light);
|
||||
border-radius: 0;
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-search {
|
||||
padding: var(--spacing-medium) var(--spacing-large);
|
||||
border: 1px solid var(--input-border);
|
||||
border-radius: var(--border-radius);
|
||||
margin: 10px 10px 5px 10px;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
font-size: var(--font-size-larger);
|
||||
}
|
||||
.multi-select .multi-select-options .multi-select-search::placeholder {
|
||||
color: var(--text-color-light);
|
||||
}
|
||||
.multi-select .multi-select-header,
|
||||
.multi-select .multi-select-option,
|
||||
.multi-select .multi-select-all {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: var(--font-size-larger);
|
||||
color: var(--text-color-dark);
|
||||
}
|
||||
.multi-select.disabled {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
background-color: var(--input-disabled);
|
||||
}
|
||||
.multi-select.multi-select-invalid .multi-select-header {
|
||||
border-color: var(--input-border-invalid);
|
||||
outline: var(--input-outline-invalid) solid 1px;
|
||||
}
|
||||
@@ -6,13 +6,13 @@
|
||||
--panel: #ffffff;
|
||||
--panel2: #f2f4fb;
|
||||
|
||||
--stroke: rgba(16, 24, 40, .10);
|
||||
--stroke2: rgba(16, 24, 40, .14);
|
||||
--stroke: #1018281a;
|
||||
--stroke2: #10182824;
|
||||
|
||||
--text: rgba(16, 24, 40, .92);
|
||||
--muted: rgba(16, 24, 40, .62);
|
||||
--text: #101828eb;
|
||||
--muted: #1018289e;
|
||||
|
||||
--shadow: 0 18px 45px rgba(16,24,40,.12);
|
||||
--shadow: 0 18px 45px #1018281f;
|
||||
--r: 14px;
|
||||
|
||||
--cardMin: 240;
|
||||
@@ -21,6 +21,9 @@
|
||||
--filterW: 170px;
|
||||
--filterWOpen: 360px;
|
||||
|
||||
--toggle-bg-color: #4281A4;
|
||||
--toggle-nub-color: #FF686B;
|
||||
|
||||
--topbarH: 64px;
|
||||
|
||||
/* Basic font reset for the component only */
|
||||
@@ -369,14 +372,70 @@
|
||||
}
|
||||
.pexpo-core-filterPanelHeader b { color: var(--text); }
|
||||
|
||||
.pexpo-core-jsonBox {
|
||||
.pexpo-core-filterPanelBody {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
overflow:auto;
|
||||
min-height: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pexpo-core-textarea {
|
||||
.pexpo-core-toggle {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pexpo-core-toggle input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pexpo-core-toggle label {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 50px;
|
||||
height: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pexpo-core-toggle label::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
background: var(--toggle-bg-off);
|
||||
border: 1.5px solid color-mix(in srgb, var(--toggle-bg-off), black var(--darker));
|
||||
box-sizing: border-box;
|
||||
transition: all 0.2s ease-in;
|
||||
}
|
||||
|
||||
.pexpo-core-toggle label::after {
|
||||
content: '';
|
||||
display: block;
|
||||
height: 21px;
|
||||
width: 21px;
|
||||
border-radius: 2px;
|
||||
background: var(--toggle-nub-color);
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
transition: all 0.2s ease-in;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.pexpo-core-toggle input:checked + label::before {
|
||||
background: var(--toggle-bg-on);
|
||||
border: 1.5px solid color-mix(in srgb, var(--toggle-bg-on), black var(--darker));
|
||||
}
|
||||
|
||||
.pexpo-core-toggle input:checked + label::after {
|
||||
left: 27px;
|
||||
}
|
||||
|
||||
/* .pexpo-core-textarea {
|
||||
width: 100%;
|
||||
height: 210px;
|
||||
resize: vertical;
|
||||
@@ -395,7 +454,7 @@
|
||||
.pexpo-core-textarea:focus {
|
||||
border-color: rgba(44,52,137,.45);
|
||||
box-shadow: 0 0 0 3px rgba(44,52,137,.14);
|
||||
}
|
||||
} */
|
||||
|
||||
.pexpo-core-hint {
|
||||
margin-top: 8px;
|
||||
|
||||
375
public/js/multiselect.js
Normal file
375
public/js/multiselect.js
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Created by David Adams
|
||||
* https://codeshack.io/multi-select-dropdown-html-javascript/
|
||||
*
|
||||
* Released under the MIT license
|
||||
*/
|
||||
class MultiSelect {
|
||||
|
||||
constructor(element, options = {}) {
|
||||
let defaults = {
|
||||
searchText: 'Search...',
|
||||
placeholder: 'Select item(s)',
|
||||
max: null,
|
||||
min: null,
|
||||
disabled: false,
|
||||
search: true,
|
||||
selectAll: true,
|
||||
listAll: true,
|
||||
closeListOnItemSelect: false,
|
||||
name: '',
|
||||
width: '',
|
||||
height: '',
|
||||
dropdownWidth: '',
|
||||
dropdownHeight: '',
|
||||
data: [],
|
||||
onChange: function() {},
|
||||
onSelect: function() {},
|
||||
onUnselect: function() {},
|
||||
onMaxReached: function() {}
|
||||
};
|
||||
this.options = Object.assign(defaults, options);
|
||||
this.selectElement = typeof element === 'string' ? document.querySelector(element) : element;
|
||||
this.originalSelectElement = this.selectElement.cloneNode(true);
|
||||
for(const prop in this.selectElement.dataset) {
|
||||
if (this.options[prop] !== undefined) {
|
||||
if (typeof this.options[prop] === 'boolean') {
|
||||
this.options[prop] = this.selectElement.dataset[prop] === 'true';
|
||||
} else {
|
||||
this.options[prop] = this.selectElement.dataset[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.name = this.selectElement.getAttribute('name') ? this.selectElement.getAttribute('name') : 'multi-select-' + Math.floor(Math.random() * 1000000);
|
||||
if (!this.options.data.length) {
|
||||
let options = this.selectElement.querySelectorAll('option');
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
this.options.data.push({
|
||||
value: options[i].value,
|
||||
text: options[i].innerHTML,
|
||||
selected: options[i].selected,
|
||||
html: options[i].getAttribute('data-html')
|
||||
});
|
||||
}
|
||||
}
|
||||
this.originalData = JSON.parse(JSON.stringify(this.options.data));
|
||||
this.element = this._template();
|
||||
this.selectElement.replaceWith(this.element);
|
||||
this.outsideClickHandler = this._outsideClick.bind(this);
|
||||
this._updateSelected();
|
||||
this._eventHandlers();
|
||||
if (this.options.disabled) {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
_template() {
|
||||
let optionsHTML = '';
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
const isSelected = this.data[i].selected;
|
||||
optionsHTML += `
|
||||
<div class="multi-select-option${isSelected ? ' multi-select-selected' : ''}" data-value="${this.data[i].value}" role="option" aria-selected="${isSelected}" tabindex="-1">
|
||||
<span class="multi-select-option-radio"></span>
|
||||
<span class="multi-select-option-text">${this.data[i].html ? this.data[i].html : this.data[i].text}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
let selectAllHTML = '';
|
||||
if (this.options.selectAll) {
|
||||
selectAllHTML = `<div class="multi-select-all" role="option" tabindex="-1">
|
||||
<span class="multi-select-option-radio"></span>
|
||||
<span class="multi-select-option-text">Select all</span>
|
||||
</div>`;
|
||||
}
|
||||
let template = `
|
||||
<div class="multi-select ${this.name}"${this.selectElement.id ? ' id="' + this.selectElement.id + '"' : ''} style="${this.width ? 'width:' + this.width + ';' : ''}${this.height ? 'height:' + this.height + ';' : ''}" role="combobox" aria-haspopup="listbox" aria-expanded="false">
|
||||
${this.selectedValues.map(value => `<input type="hidden" name="${this.name}[]" value="${value}">`).join('')}
|
||||
<div class="multi-select-header" style="${this.width ? 'width:' + this.width + ';' : ''}${this.height ? 'height:' + this.height + ';' : ''}" tabindex="0">
|
||||
<span class="multi-select-header-max">${this.options.max ? this.selectedValues.length + '/' + this.options.max : ''}</span>
|
||||
<span class="multi-select-header-placeholder">${this.placeholder}</span>
|
||||
</div>
|
||||
<div class="multi-select-options" style="${this.options.dropdownWidth ? 'width:' + this.options.dropdownWidth + ';' : ''}${this.options.dropdownHeight ? 'height:' + this.options.dropdownHeight + ';' : ''}" role="listbox">
|
||||
${this.options.search ? '<input type="text" class="multi-select-search" placeholder="' + this.searchText + '" role="searchbox">' : ''}
|
||||
${selectAllHTML}
|
||||
${optionsHTML}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
let element = document.createElement('div');
|
||||
element.innerHTML = template;
|
||||
return element.firstElementChild;
|
||||
}
|
||||
|
||||
_eventHandlers() {
|
||||
let headerElement = this.element.querySelector('.multi-select-header');
|
||||
const toggleDropdown = (forceClose = false) => {
|
||||
if (this.element.classList.contains('disabled')) return;
|
||||
if (forceClose || headerElement.classList.contains('multi-select-header-active')) {
|
||||
headerElement.classList.remove('multi-select-header-active');
|
||||
this.element.setAttribute('aria-expanded', 'false');
|
||||
} else {
|
||||
headerElement.classList.add('multi-select-header-active');
|
||||
this.element.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
};
|
||||
this.element.querySelectorAll('.multi-select-option').forEach(option => {
|
||||
option.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
if (this.element.classList.contains('disabled')) return;
|
||||
let selected = true;
|
||||
if (!option.classList.contains('multi-select-selected')) {
|
||||
if (this.options.max && this.selectedValues.length >= this.options.max) {
|
||||
this.options.onMaxReached(this.options.max);
|
||||
return;
|
||||
}
|
||||
option.classList.add('multi-select-selected');
|
||||
option.setAttribute('aria-selected', 'true');
|
||||
this.element.insertAdjacentHTML('afterbegin', `<input type="hidden" name="${this.name}[]" value="${option.dataset.value}">`);
|
||||
this.data.find(data => data.value == option.dataset.value).selected = true;
|
||||
} else {
|
||||
option.classList.remove('multi-select-selected');
|
||||
option.setAttribute('aria-selected', 'false');
|
||||
this.element.querySelector(`input[value="${option.dataset.value}"]`).remove();
|
||||
this.data.find(data => data.value == option.dataset.value).selected = false;
|
||||
selected = false;
|
||||
}
|
||||
this._updateHeader();
|
||||
if (this.options.search) {
|
||||
this.element.querySelector('.multi-select-search').value = '';
|
||||
this.element.querySelectorAll('.multi-select-option').forEach(opt => opt.style.display = 'flex');
|
||||
}
|
||||
if (this.options.closeListOnItemSelect) {
|
||||
toggleDropdown(true);
|
||||
}
|
||||
this.options.onChange(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option);
|
||||
if (selected) {
|
||||
this.options.onSelect(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option);
|
||||
} else {
|
||||
this.options.onUnselect(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option);
|
||||
}
|
||||
this._validate();
|
||||
};
|
||||
});
|
||||
headerElement.onclick = () => toggleDropdown();
|
||||
if (this.options.search) {
|
||||
let search = this.element.querySelector('.multi-select-search');
|
||||
search.oninput = () => {
|
||||
this.element.querySelectorAll('.multi-select-option').forEach(option => {
|
||||
const text = option.querySelector('.multi-select-option-text').innerHTML.toLowerCase();
|
||||
option.style.display = text.includes(search.value.toLowerCase()) ? 'flex' : 'none';
|
||||
});
|
||||
};
|
||||
}
|
||||
if (this.options.selectAll) {
|
||||
let selectAllButton = this.element.querySelector('.multi-select-all');
|
||||
selectAllButton.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
if (this.element.classList.contains('disabled')) return;
|
||||
let allSelected = selectAllButton.classList.contains('multi-select-selected');
|
||||
this.element.querySelectorAll('.multi-select-option').forEach(option => {
|
||||
let dataItem = this.data.find(data => data.value == option.dataset.value);
|
||||
if (dataItem && ((allSelected && dataItem.selected) || (!allSelected && !dataItem.selected))) {
|
||||
option.click();
|
||||
}
|
||||
});
|
||||
selectAllButton.classList.toggle('multi-select-selected');
|
||||
};
|
||||
}
|
||||
if (this.selectElement.id && document.querySelector('label[for="' + this.selectElement.id + '"]')) {
|
||||
document.querySelector('label[for="' + this.selectElement.id + '"]').onclick = () => {
|
||||
toggleDropdown();
|
||||
};
|
||||
}
|
||||
document.addEventListener('click', this.outsideClickHandler);
|
||||
headerElement.addEventListener('keydown', (e) => {
|
||||
if (['Enter', ' ', 'ArrowDown', 'ArrowUp'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
toggleDropdown();
|
||||
const firstElement = this.element.querySelector('[role="searchbox"]') || this.element.querySelector('[role="option"]');
|
||||
if (firstElement) firstElement.focus();
|
||||
}
|
||||
});
|
||||
this.element.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
toggleDropdown(true);
|
||||
headerElement.focus();
|
||||
}
|
||||
});
|
||||
const optionsContainer = this.element.querySelector('.multi-select-options');
|
||||
optionsContainer.addEventListener('keydown', (e) => {
|
||||
const currentFocused = document.activeElement;
|
||||
if (currentFocused.closest('.multi-select-options')) {
|
||||
if (['ArrowDown', 'ArrowUp'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
const direction = e.key === 'ArrowDown' ? 'nextElementSibling' : 'previousElementSibling';
|
||||
let nextElement = currentFocused[direction];
|
||||
while (nextElement && (nextElement.style.display === 'none' || !nextElement.matches('[role="option"], [role="searchbox"]'))) {
|
||||
nextElement = nextElement[direction];
|
||||
}
|
||||
if (nextElement) nextElement.focus();
|
||||
} else if (['Enter', ' '].includes(e.key) && currentFocused.matches('[role="option"]')) {
|
||||
e.preventDefault();
|
||||
currentFocused.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_updateHeader() {
|
||||
this.element.querySelectorAll('.multi-select-header-option, .multi-select-header-placeholder').forEach(el => el.remove());
|
||||
if (this.selectedValues.length > 0) {
|
||||
if (this.options.listAll) {
|
||||
this.selectedItems.forEach(item => {
|
||||
const el = document.createElement('span');
|
||||
el.className = 'multi-select-header-option';
|
||||
el.dataset.value = item.value;
|
||||
el.innerHTML = item.text;
|
||||
this.element.querySelector('.multi-select-header').prepend(el);
|
||||
});
|
||||
} else {
|
||||
this.element.querySelector('.multi-select-header').insertAdjacentHTML('afterbegin', `<span class="multi-select-header-option">${this.selectedValues.length} selected</span>`);
|
||||
}
|
||||
} else {
|
||||
this.element.querySelector('.multi-select-header').insertAdjacentHTML('beforeend', `<span class="multi-select-header-placeholder">${this.placeholder}</span>`);
|
||||
}
|
||||
if (this.options.max) {
|
||||
this.element.querySelector('.multi-select-header-max').innerHTML = this.selectedValues.length + '/' + this.options.max;
|
||||
}
|
||||
}
|
||||
|
||||
_updateSelected() { this._updateHeader(); }
|
||||
|
||||
_validate() {
|
||||
if (this.options.min && this.selectedValues.length < this.options.min) {
|
||||
this.element.classList.add('multi-select-invalid');
|
||||
} else {
|
||||
this.element.classList.remove('multi-select-invalid');
|
||||
}
|
||||
}
|
||||
|
||||
_outsideClick(event) {
|
||||
if (!this.element.contains(event.target) && !event.target.closest('label[for="' + this.selectElement.id + '"]')) {
|
||||
let headerElement = this.element.querySelector('.multi-select-header');
|
||||
if (headerElement.classList.contains('multi-select-header-active')) {
|
||||
headerElement.classList.remove('multi-select-header-active');
|
||||
this.element.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select(value) {
|
||||
const option = this.element.querySelector(`.multi-select-option[data-value="${value}"]`);
|
||||
if (option && !option.classList.contains('multi-select-selected')) {
|
||||
option.click();
|
||||
}
|
||||
}
|
||||
|
||||
unselect(value) {
|
||||
const option = this.element.querySelector(`.multi-select-option[data-value="${value}"]`);
|
||||
if (option && option.classList.contains('multi-select-selected')) {
|
||||
option.click();
|
||||
}
|
||||
}
|
||||
|
||||
setValues(values) {
|
||||
this.data.forEach(item => {
|
||||
item.selected = values.includes(item.value);
|
||||
});
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.element.classList.add('disabled');
|
||||
this.element.querySelector('.multi-select-header').removeAttribute('tabindex');
|
||||
const searchInput = this.element.querySelector('.multi-select-search');
|
||||
if (searchInput) searchInput.disabled = true;
|
||||
}
|
||||
|
||||
enable() {
|
||||
this.element.classList.remove('disabled');
|
||||
this.element.querySelector('.multi-select-header').setAttribute('tabindex', '0');
|
||||
const searchInput = this.element.querySelector('.multi-select-search');
|
||||
if (searchInput) searchInput.disabled = false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.element.replaceWith(this.originalSelectElement);
|
||||
document.removeEventListener('click', this.outsideClickHandler);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
const newElement = this._template();
|
||||
this.element.replaceWith(newElement);
|
||||
this.element = newElement;
|
||||
this._updateSelected();
|
||||
this._eventHandlers();
|
||||
this._validate();
|
||||
}
|
||||
|
||||
addItem(item) {
|
||||
this.options.data.push(item);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
addItems(items) {
|
||||
this.options.data.push(...items);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
async fetch(url, options = {}) {
|
||||
const response = await fetch(url, options);
|
||||
const data = await response.json();
|
||||
this.addItems(data);
|
||||
if (this.options.onload) {
|
||||
this.options.onload(data, this.options);
|
||||
}
|
||||
}
|
||||
|
||||
removeItem(value) {
|
||||
this.options.data = this.options.data.filter(item => item.value !== value);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.options.data = [];
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.data = JSON.parse(JSON.stringify(this.originalData));
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this.data.forEach(item => item.selected = true);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
get selectedValues() { return this.data.filter(d => d.selected).map(d => d.value); }
|
||||
get selectedItems() { return this.data.filter(d => d.selected); }
|
||||
get data() { return this.options.data; }
|
||||
set data(value) { this.options.data = value; }
|
||||
|
||||
set selectElement(value) { this.options.selectElement = value; }
|
||||
get selectElement() { return this.options.selectElement; }
|
||||
|
||||
set element(value) { this.options.element = value; }
|
||||
get element() { return this.options.element; }
|
||||
|
||||
set searchText(value) { this.options.searchText = value; }
|
||||
get searchText() { return this.options.searchText; }
|
||||
|
||||
set placeholder(value) { this.options.placeholder = value; }
|
||||
get placeholder() { return this.options.placeholder; }
|
||||
|
||||
set name(value) { this.options.name = value; }
|
||||
get name() { return this.options.name; }
|
||||
|
||||
set width(value) { this.options.width = value; }
|
||||
get width() { return this.options.width; }
|
||||
|
||||
set height(value) { this.options.height = value; }
|
||||
get height() { return this.options.height; }
|
||||
|
||||
}
|
||||
document.querySelectorAll('[data-multi-select]').forEach(select => new MultiSelect(select));
|
||||
@@ -1,54 +1,67 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// ---------- DOM Elements ----------
|
||||
const shell = document.getElementById("shell");
|
||||
const topbar = document.getElementById("topbar");
|
||||
const shell = document.getElementById("pexpo-core-shell");
|
||||
const topbar = document.getElementById("pexpo-core-topbar");
|
||||
|
||||
const filterWrap = document.getElementById("filterWrap");
|
||||
const filterBtn = document.getElementById("filterBtn");
|
||||
const filterDrawer = document.getElementById("filterDrawer");
|
||||
const drawerBackdrop = document.getElementById("drawerBackdrop");
|
||||
const filterWrap = document.getElementById("pexpo-core-filterWrap");
|
||||
const filterBtn = document.getElementById("pexpo-core-filterBtn");
|
||||
const filterDrawer = document.getElementById("pexpo-core-filterDrawer");
|
||||
const drawerBackdrop = document.getElementById("pexpo-core-drawerBackdrop");
|
||||
|
||||
const filterJson = document.getElementById("filterJson");
|
||||
const jsonError = document.getElementById("jsonError");
|
||||
const filterLabel = document.getElementById("filterLabel");
|
||||
// const filterBody = document.getElementById("pexpo-core-filterBody");
|
||||
// const filterLabel = document.getElementById("pexpo-core-filterLabel");
|
||||
const filterFTag = document.getElementById("pexpo-core-tag-toggle");
|
||||
const filterTags = document.getElementById("pexpo-core-tags");
|
||||
|
||||
const qInput = document.getElementById("q");
|
||||
const masonry = document.getElementById("masonry");
|
||||
const measure = document.getElementById("measure");
|
||||
const qInput = document.getElementById("pexpo-core-q");
|
||||
const masonry = document.getElementById("pexpo-core-masonry");
|
||||
const measure = document.getElementById("pexpo-core-measure");
|
||||
|
||||
const count = document.getElementById("count");
|
||||
const layoutMeta = document.getElementById("layoutMeta");
|
||||
const sortSelect = document.getElementById("sortSelect");
|
||||
const count = document.getElementById("pexpo-core-count");
|
||||
const layoutMeta = document.getElementById("pexpo-core-layoutMeta");
|
||||
const sortSelect = document.getElementById("pexpo-core-sortSelect");
|
||||
|
||||
// ---------- State ----------
|
||||
let filters = {
|
||||
q: "",
|
||||
sort: "relevance",
|
||||
status: ["active", "archived"],
|
||||
tags: ["ui", "mock"],
|
||||
maxResults: 20
|
||||
force_tags: false,
|
||||
tags: [],
|
||||
resultsPerPage: 20,
|
||||
page: 1,
|
||||
};
|
||||
|
||||
let activeSortKey = "relevance";
|
||||
let results = []; // Fills via Ajax
|
||||
let results = [];
|
||||
|
||||
// ---------- Sort Definitions ---------- LEFTOVER FROM TESTING REMOVE IN PROD
|
||||
const SORTS = {
|
||||
relevance: {
|
||||
label: "Relevance",
|
||||
compare: (a, b) => (b.relevance - a.relevance) || (b.score - a.score)
|
||||
},
|
||||
score_desc: {
|
||||
label: "Score (high → low)",
|
||||
compare: (a, b) => (b.score - a.score)
|
||||
},
|
||||
date_desc: {
|
||||
label: "Date (new → old)",
|
||||
compare: (a, b) => new Date(b.updated) - new Date(a.updated)
|
||||
},
|
||||
title_asc: {
|
||||
label: "Title (A → Z)",
|
||||
compare: (a, b) => (a.title || "").localeCompare(b.title || "")
|
||||
// ---------- Initialize MultiSelect Library ----------
|
||||
if (typeof MultiSelect !== 'undefined') {
|
||||
new MultiSelect(filterTags, {
|
||||
// max: 20,
|
||||
search: true,
|
||||
selectAll: false,
|
||||
onSelect: function(value) {
|
||||
if (!filters.tags.includes(value)) {
|
||||
filters.tags.push(value);
|
||||
}
|
||||
console.log('Updated filters.tags:', filters.tags);
|
||||
},
|
||||
onUnselect: function(value) {
|
||||
filters.tags = filters.tags.filter(tag => tag !== value);
|
||||
console.log('Updated filters.tags:', filters.tags);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn("MultiSelect library not found. Filters will not work without it.");
|
||||
}
|
||||
|
||||
// ---------- Sort Definitions ----------
|
||||
const SORTS = {
|
||||
relevance: "Relevánsság (legjobb → legrosszabb)",
|
||||
date_desc: "Dátum (új → régi)",
|
||||
date_asc: "Dátum (régi → új)",
|
||||
title_asc: "Cím (A → Z)",
|
||||
title_desc: "Cím (Z → A)",
|
||||
};
|
||||
|
||||
// ---------- Helpers ----------
|
||||
@@ -58,6 +71,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}[s]));
|
||||
}
|
||||
|
||||
function encodeDataToURL(data) {
|
||||
return Object
|
||||
.keys(data)
|
||||
.map(value => `${value}=${encodeURIComponent(data[value])}`)
|
||||
.join('&');
|
||||
}
|
||||
|
||||
function cardEl(r) {
|
||||
const div = document.createElement("div");
|
||||
div.className = "pexpo-core-card";
|
||||
@@ -86,12 +106,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
// ---------- Ajax Function ----------
|
||||
async function fetchData(query = "") {
|
||||
async function fetchData() {
|
||||
// 1. Show loading state if needed
|
||||
masonry.style.opacity = "0.5";
|
||||
|
||||
try {
|
||||
const response = await fetch(`/wp-json/pexpo/v1/query?q=${encodeURIComponent(query)}&limit=${filters.maxResults}`);
|
||||
const response = await fetch(`/wp-json/pexpo/v1/query?${encodeDataToURL(filters)}`);
|
||||
const data = await response.json();
|
||||
|
||||
results = data;
|
||||
@@ -106,21 +126,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// FOR TESTING ONLY REMOVE IN PROD
|
||||
// function computeLocalRelevance(query) {
|
||||
// if (!query) {
|
||||
// results.forEach(r => r.relevance = r.score);
|
||||
// return;
|
||||
// }
|
||||
// const q = query.toLowerCase();
|
||||
// results.forEach(r => {
|
||||
// let hits = 0;
|
||||
// if (r.title.toLowerCase().includes(q)) hits += 50;
|
||||
// if (r.desc.toLowerCase().includes(q)) hits += 20;
|
||||
// r.relevance = hits + r.score;
|
||||
// });
|
||||
// }
|
||||
|
||||
// ---------- Layout & Masonry Logic ----------
|
||||
const minCard = () => Number(getComputedStyle(document.documentElement).getPropertyValue("--cardMin")) || 240;
|
||||
const gap = () => Number(getComputedStyle(document.documentElement).getPropertyValue("--gap")) || 12;
|
||||
@@ -215,7 +220,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
colEls[p.col].appendChild(cardEl(p.item.r));
|
||||
}
|
||||
|
||||
layoutMeta.textContent = `${cols} cols • ${SORTS[activeSortKey]?.label || "Relevance"}`;
|
||||
// layoutMeta.textContent = `${cols} oszlop • ${SORTS[activeSortKey]?.label || "Relevance"}`;
|
||||
}
|
||||
|
||||
// ---------- UI Events ----------
|
||||
@@ -223,22 +228,35 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// Sort Dropdown
|
||||
if (sortSelect) {
|
||||
sortSelect.innerHTML = Object.entries(SORTS)
|
||||
.map(([k, def]) => `<option value="${escapeHtml(k)}">${escapeHtml(def.label)}</option>`)
|
||||
.map(([k, label]) => `<option value="${escapeHtml(k)}">${escapeHtml(label)}</option>`)
|
||||
.join("");
|
||||
sortSelect.value = activeSortKey;
|
||||
sortSelect.addEventListener("change", () => {
|
||||
activeSortKey = sortSelect.value;
|
||||
requestLayout();
|
||||
filters.sort = activeSortKey;
|
||||
fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
// Search
|
||||
qInput.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
fetchData(qInput.value.trim());
|
||||
filters.q = qInput.value.trim();
|
||||
fetchData();
|
||||
}
|
||||
});
|
||||
|
||||
// Tag Toggle
|
||||
filterFTag.addEventListener("change", () => {
|
||||
filters.force_tags = filterFTag.checked;
|
||||
});
|
||||
|
||||
// Tags multiselect
|
||||
filterTags.addEventListener("change", (value, text, element) => {
|
||||
console.log('Change:', value, text, element);
|
||||
});
|
||||
|
||||
|
||||
// Filter Drawer Logic
|
||||
function syncTopbarHeight() {
|
||||
const h = topbar.offsetHeight;
|
||||
@@ -256,7 +274,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
shell.classList.toggle("pexpo-core-drawerOpen", open);
|
||||
|
||||
syncTopbarHeight();
|
||||
if (open) filterJson.focus();
|
||||
}
|
||||
|
||||
filterBtn.addEventListener("click", () => setOpen(!filterDrawer.classList.contains("pexpo-core-open")));
|
||||
@@ -265,30 +282,31 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (e.key === "Escape" && filterDrawer.classList.contains("pexpo-core-open")) setOpen(false);
|
||||
});
|
||||
|
||||
// Apply JSON Filter
|
||||
function applyJson() {
|
||||
try {
|
||||
const parsed = JSON.parse(filterJson.value);
|
||||
jsonError.classList.remove("pexpo-core-show");
|
||||
filters = parsed;
|
||||
filterLabel.textContent = (filters.category || "Custom");
|
||||
// // Apply JSON Filter
|
||||
// function applyJson() {
|
||||
// try {
|
||||
// const parsed = JSON.parse(filterJson.value);
|
||||
// jsonError.classList.remove("pexpo-core-show");
|
||||
// filters = parsed;
|
||||
// filterLabel.textContent = (filters.category || "Custom");
|
||||
|
||||
// Trigger new search with new filters
|
||||
fetchData(qInput.value.trim());
|
||||
// // Trigger new search with new filters
|
||||
// filters.q = qInput.value.trim();
|
||||
// fetchData();
|
||||
|
||||
// Close drawer on success (optional)
|
||||
// setOpen(false);
|
||||
} catch (err) {
|
||||
jsonError.classList.add("pexpo-core-show");
|
||||
}
|
||||
}
|
||||
// // Close drawer on success (optional)
|
||||
// // setOpen(false);
|
||||
// } catch (err) {
|
||||
// jsonError.classList.add("pexpo-core-show");
|
||||
// }
|
||||
// }
|
||||
|
||||
filterJson.addEventListener("keydown", (e) => {
|
||||
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
applyJson();
|
||||
}
|
||||
});
|
||||
// filterJson.addEventListener("keydown", (e) => {
|
||||
// if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
||||
// e.preventDefault();
|
||||
// applyJson();
|
||||
// }
|
||||
// });
|
||||
|
||||
// Resize Observer
|
||||
const ro = new ResizeObserver(() => {
|
||||
@@ -299,8 +317,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
ro.observe(masonry);
|
||||
|
||||
// Initial Load
|
||||
initFilters();
|
||||
syncTopbarHeight();
|
||||
fetchData("");
|
||||
fetchData();
|
||||
|
||||
// --- Temporary Mock Response Generator (DELETE ME IN PRODUCTION) ---
|
||||
function simulateBackendResponse(q) {
|
||||
|
||||
@@ -32,42 +32,42 @@
|
||||
<div class="pexpo-core-searchWrap pexpo-core-topHeight">
|
||||
<div class="pexpo-core-search" role="search">
|
||||
<span class="pexpo-core-icons-search" aria-hidden="true"></span>
|
||||
<form id="qForm" action="javascript:void(0)">
|
||||
<input id="q" placeholder="Keresés..." autocomplete="off" />
|
||||
<form id="pexpo-core-qForm" action="javascript:void(0)">
|
||||
<input id="pexpo-core-q" placeholder="Keresés..." autocomplete="off" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pexpo-core-filterDrawerBackdrop" id="drawerBackdrop" aria-hidden="true"></div>
|
||||
<div class="pexpo-core-filterDrawer" id="filterDrawer" aria-hidden="true">
|
||||
<div class="pexpo-core-filterDrawerBackdrop pexpo-core-show " id="pexpo-core-drawerBackdrop" aria-hidden="true"></div>
|
||||
<div class="pexpo-core-filterDrawer" id="pexpo-core-filterDrawer" aria-hidden="true">
|
||||
<div class="pexpo-core-filterPanelHeader">
|
||||
<span><b>Filter JSON</b> (edit + apply)</span>
|
||||
<span class="pexpo-core-pill" id="applyHint">Ctrl + Enter</span>
|
||||
<span><b>Filters</b></span>
|
||||
</div>
|
||||
<div class="pexpo-core-jsonBox">
|
||||
<textarea id="filterJson" class="pexpo-core-textarea" spellcheck="false">
|
||||
{
|
||||
"category": "all",
|
||||
"sort": "relevance",
|
||||
"status": ["active", "archived"],
|
||||
"minScore": 0,
|
||||
"tags": ["ui", "mock"],
|
||||
"maxResults": 17
|
||||
}
|
||||
</textarea>
|
||||
<div class="pexpo-core-error" id="jsonError">Invalid JSON. Fix the syntax and try again.</div>
|
||||
<div class="pexpo-core-filterPanelBody">
|
||||
<div class="pexpo-core-toggle">
|
||||
<span class="toggle-text">Címke kötelezése</span>
|
||||
<div class="toggle-wrapper">
|
||||
<input type="checkbox" id="pexpo-core-tag-toggle">
|
||||
<label for="pexpo-core-tag-toggle"></label>
|
||||
</div>
|
||||
</div>
|
||||
<label for="pexpo-core-tags">Címkék</label>
|
||||
<select id="pexpo-core-tags" data-placeholder="Opciók kiválasztása" multiple="multiple">
|
||||
<!-- <option value="option1">Option 1</option>
|
||||
<option value="option2">Option 2</option>
|
||||
<option value="option3">Option 3</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pexpo-core-body">
|
||||
<div class="pexpo-core-metaRow">
|
||||
<div id="metaLeft"><b id="count">0</b> találat</div>
|
||||
<div id="metaRight">Elrendezés: <span id="layoutMeta">—</span></div>
|
||||
<div id="pexpo-core-metaLeft"><b id="pexpo-core-count">0</b> találat</div>
|
||||
<div id="pexpo-core-metaRight">Elrendezés: <span id="pexpo-core-layoutMeta">—</span></div>
|
||||
</div>
|
||||
|
||||
<div class="pexpo-core-masonry" id="masonry" aria-live="polite"></div>
|
||||
|
||||
<div id="measure" style="position:absolute; left:-9999px; top:-9999px; width:300px; visibility:hidden;"></div>
|
||||
<div class="pexpo-core-masonry" id="pexpo-core-masonry" aria-live="polite"></div>
|
||||
<div id="pexpo-core-measure" style="position:absolute; left:-9999px; top:-9999px; width:300px; visibility:hidden;"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,520 +4,66 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Mock Search UI (Masonry)</title>
|
||||
<link rel="stylesheet" href="css/multiselect.css">
|
||||
<style>
|
||||
.pexpo-core-root {
|
||||
--accent1: #950000;
|
||||
--accent2: #2c3489;
|
||||
|
||||
--bg: #f6f7fb;
|
||||
--panel: #ffffff;
|
||||
--panel2: #f2f4fb;
|
||||
|
||||
--stroke: rgba(16, 24, 40, .10);
|
||||
--stroke2: rgba(16, 24, 40, .14);
|
||||
|
||||
--text: rgba(16, 24, 40, .92);
|
||||
--muted: rgba(16, 24, 40, .62);
|
||||
|
||||
--shadow: 0 18px 45px rgba(16,24,40,.12);
|
||||
--r: 14px;
|
||||
|
||||
--cardMin: 240;
|
||||
--gap: 12px;
|
||||
|
||||
--filterW: 170px;
|
||||
--filterWOpen: 360px;
|
||||
|
||||
--topbarH: 64px;
|
||||
|
||||
/* Basic font reset for the component only */
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
|
||||
color: var(--text);
|
||||
line-height: 1.35;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Scoped box-sizing reset */
|
||||
.pexpo-core-root * { box-sizing: border-box; }
|
||||
|
||||
.pexpo-core-shell {
|
||||
width: min(1100px, 96vw);
|
||||
height: min(720px, 92vh);
|
||||
background: linear-gradient(180deg, rgba(44,52,137,.05), transparent 42%), var(--panel);
|
||||
border: 1px solid var(--stroke);
|
||||
border-radius: calc(var(--r) + 2px);
|
||||
box-shadow: var(--shadow);
|
||||
overflow: hidden;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
/* --- Top row --- */
|
||||
.pexpo-core-topbar {
|
||||
display:flex;
|
||||
align-items:flex-start;
|
||||
justify-content:space-between;
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
border-bottom: 1px solid var(--stroke);
|
||||
background: linear-gradient(180deg, rgba(16,24,40,.02), rgba(16,24,40,.00));
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.pexpo-core-topHeight {
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
/* Filter button */
|
||||
.pexpo-core-filterWrap {
|
||||
width: var(--filterW);
|
||||
transition: width 220ms ease;
|
||||
}
|
||||
.pexpo-core-filterWrap.pexpo-core-open { width: var(--filterWOpen); }
|
||||
|
||||
.pexpo-core-filterBtn {
|
||||
width: 100%;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap: 10px;
|
||||
padding: 10px 10px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--stroke2);
|
||||
background: var(--panel2);
|
||||
color: var(--text);
|
||||
cursor:pointer;
|
||||
user-select:none;
|
||||
box-shadow: 0 8px 22px rgba(16,24,40,.10);
|
||||
}
|
||||
|
||||
.pexpo-core-filterBtn .pexpo-core-left {
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pexpo-core-pill {
|
||||
font-size: 12px;
|
||||
padding: 3px 8px;
|
||||
border: 1px solid rgba(44,52,137,.25);
|
||||
border-radius: 999px;
|
||||
color: rgba(44,52,137,.85);
|
||||
background: rgba(44,52,137,.06);
|
||||
white-space:nowrap;
|
||||
}
|
||||
|
||||
.pexpo-core-filterBtn .pexpo-core-label {
|
||||
font-weight: 650;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Sort */
|
||||
.pexpo-core-sortWrap {
|
||||
display:flex;
|
||||
justify-content: space-between;
|
||||
align-items:center;
|
||||
gap: 10px;
|
||||
padding: 10px 10px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--stroke2);
|
||||
background: var(--panel2);
|
||||
color: var(--text);
|
||||
box-shadow: 0 8px 22px rgba(16,24,40,.10);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.pexpo-core-sortLabel {
|
||||
font-weight: 650;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.pexpo-core-sortSelect {
|
||||
border: 1px solid rgba(16,24,40,.16);
|
||||
background: rgba(255,255,255,.95);
|
||||
color: var(--text);
|
||||
border-radius: 10px;
|
||||
padding: 5px 10px;
|
||||
outline: none;
|
||||
font: inherit;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.pexpo-core-sortSelect:focus {
|
||||
border-color: rgba(44,52,137,.45);
|
||||
box-shadow: 0 0 0 3px rgba(44,52,137,.14);
|
||||
}
|
||||
|
||||
/* Search */
|
||||
.pexpo-core-searchWrap {
|
||||
flex: 1 1 auto;
|
||||
display:flex;
|
||||
justify-content:flex-end;
|
||||
align-items:flex-start;
|
||||
}
|
||||
|
||||
.pexpo-core-search {
|
||||
width: min(520px, 100%);
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--stroke2);
|
||||
background: rgba(255,255,255,.9);
|
||||
box-shadow: 0 8px 22px rgba(16,24,40,.10);
|
||||
}
|
||||
|
||||
.pexpo-core-search input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pexpo-core-kbd {
|
||||
color: rgba(16,24,40,.62);
|
||||
border: 1px solid rgba(16,24,40,.16);
|
||||
border-bottom-color: rgba(16,24,40,.22);
|
||||
background: rgba(255,255,255,.85);
|
||||
border-radius: 8px;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* --- Main body --- */
|
||||
.pexpo-core-body {
|
||||
flex: 1 1 auto;
|
||||
padding: 14px;
|
||||
overflow:auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.pexpo-core-metaRow {
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Masonry */
|
||||
.pexpo-core-masonry {
|
||||
display:flex;
|
||||
gap: var(--gap);
|
||||
align-items:flex-start;
|
||||
}
|
||||
|
||||
.pexpo-core-mCol {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap: var(--gap);
|
||||
}
|
||||
|
||||
.pexpo-core-card {
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--stroke2);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(143, 152, 255, 0.05), rgba(255,255,255,.92));
|
||||
padding: 12px;
|
||||
box-shadow: 0 12px 26px rgba(16,24,40,.10);
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pexpo-core-imageWrap {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pexpo-core-imageWrap img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pexpo-core-cardTop {
|
||||
display:flex;
|
||||
align-items:flex-start;
|
||||
justify-content:space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pexpo-core-title {
|
||||
font-weight: 750;
|
||||
letter-spacing: .15px;
|
||||
color: var(--text);
|
||||
&:hover {
|
||||
color: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.pexpo-core-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
row-gap: 6px;
|
||||
}
|
||||
|
||||
.pexpo-core-tag {
|
||||
font-size: 12px;
|
||||
padding: 3px 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #333333ce;
|
||||
color: var(--text);
|
||||
background: #99999930;
|
||||
white-space:nowrap;
|
||||
}
|
||||
|
||||
.pexpo-core-desc {
|
||||
color: rgba(16,24,40,.74);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pexpo-core-foot {
|
||||
margin-top:auto;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
color: rgba(16,24,40,.60);
|
||||
font-size: 12px;
|
||||
border-top: 1px dashed rgba(16,24,40,.16);
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.pexpo-core-dot {
|
||||
width: 7px; height: 7px;
|
||||
border-radius: 999px;
|
||||
background: rgba(44,52,137,.55);
|
||||
display:inline-block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.pexpo-core-status {
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* Filter drawer */
|
||||
.pexpo-core-filterDrawerBackdrop {
|
||||
position:absolute;
|
||||
inset: var(--topbarH) 0 0 0;
|
||||
background: rgba(16,24,40,.20);
|
||||
backdrop-filter: blur(2px);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 180ms ease;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.pexpo-core-filterDrawerBackdrop.pexpo-core-show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.pexpo-core-filterDrawer {
|
||||
position:absolute;
|
||||
top: var(--topbarH);
|
||||
bottom: 0;
|
||||
left: 14px;
|
||||
width: var(--filterW);
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--stroke2);
|
||||
background: rgba(255,255,255,.92);
|
||||
overflow:hidden;
|
||||
box-shadow: 0 18px 45px rgba(16,24,40,.18);
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
pointer-events: none;
|
||||
transition: opacity 200ms ease, transform 200ms ease, width 220ms ease;
|
||||
z-index: 3;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
min-height: 0;
|
||||
margin: 12.5px 0 12.5px 0;
|
||||
}
|
||||
|
||||
.pexpo-core-filterDrawer.pexpo-core-open {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
width: var(--filterWOpen);
|
||||
}
|
||||
|
||||
.pexpo-core-filterPanelHeader {
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
padding: 10px 10px;
|
||||
border-bottom: 1px solid var(--stroke);
|
||||
color: rgba(16,24,40,.60);
|
||||
background: linear-gradient(180deg, rgba(44,52,137,.05), rgba(255,255,255,.0));
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.pexpo-core-filterPanelHeader b { color: var(--text); }
|
||||
|
||||
.pexpo-core-jsonBox {
|
||||
padding: 10px;
|
||||
flex: 1 1 auto;
|
||||
overflow:auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.pexpo-core-textarea {
|
||||
width: 100%;
|
||||
height: 210px;
|
||||
resize: vertical;
|
||||
min-height: 180px;
|
||||
max-height: 60vh;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(44,52,137,.20);
|
||||
background: #fbfbfe;
|
||||
color: rgba(16,24,40,.92);
|
||||
padding: 10px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
outline: none;
|
||||
}
|
||||
.pexpo-core-textarea:focus {
|
||||
border-color: rgba(44,52,137,.45);
|
||||
box-shadow: 0 0 0 3px rgba(44,52,137,.14);
|
||||
}
|
||||
|
||||
.pexpo-core-hint {
|
||||
margin-top: 8px;
|
||||
color: rgba(16,24,40,.60);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pexpo-core-error {
|
||||
margin-top: 8px;
|
||||
color: #950000;
|
||||
font-size: 12px;
|
||||
display:none;
|
||||
}
|
||||
.pexpo-core-error.pexpo-core-show { display:block; }
|
||||
|
||||
/* Drawer open padding */
|
||||
.pexpo-core-shell.pexpo-core-drawerOpen .pexpo-core-body {
|
||||
/* padding-left: calc(14px + var(--filterWOpen) + 14px); */
|
||||
transition: padding-left 220ms ease;
|
||||
}
|
||||
.pexpo-core-shell:not(.pexpo-core-drawerOpen) .pexpo-core-body {
|
||||
transition: padding-left 220ms ease;
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
|
||||
.pexpo-core-icons-search {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(var(--ggs, 1));
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid;
|
||||
border-radius: 100%;
|
||||
margin-left: -4px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
.pexpo-core-icons-search:after {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
width: 3px;
|
||||
height: 8px;
|
||||
background: currentColor;
|
||||
transform: rotate(-45deg);
|
||||
top: 10px;
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.pexpo-core-icons-down {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(var(--ggs, 1));
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.pexpo-core-icons-down:after {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-bottom: 2px solid;
|
||||
border-right: 2px solid;
|
||||
transform: rotate(45deg);
|
||||
transition: 1s transform ease;
|
||||
left: 4px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.pexpo-core-filterWrap.pexpo-core-open .pexpo-core-icons-down {
|
||||
transform: rotate(-135deg) translate(-1px,-1px);
|
||||
}
|
||||
|
||||
.pexpo-core-icons-options {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(var(--ggs, 1));
|
||||
width: 10px;
|
||||
height: 2px;
|
||||
box-shadow:
|
||||
-3px 4px 0 0,
|
||||
3px -4px 0 0;
|
||||
margin: 0 10px 0 10px;
|
||||
}
|
||||
.pexpo-core-icons-options::after,
|
||||
.pexpo-core-icons-options::before {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border: 2px solid;
|
||||
border-radius: 100%;
|
||||
}
|
||||
.pexpo-core-icons-options::before {
|
||||
top: -7px;
|
||||
left: -4px;
|
||||
}
|
||||
.pexpo-core-icons-options::after {
|
||||
bottom: -7px;
|
||||
right: -4px;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
/* [Core CSS - Same as before] */
|
||||
.pexpo-core-root { --accent1: #950000; --accent2: #2c3489; --darker: 30%; --lighter: 30%; --bg: #f6f7fb; --panel: #ffffff; --panel2: #f2f4fb; --stroke: rgba(16, 24, 40, .10); --stroke2: rgba(16, 24, 40, .14); --text: rgba(16, 24, 40, .92); --muted: rgba(16, 24, 40, .62); --shadow: 0 18px 45px rgba(16,24,40,.12); --r: 14px; --cardMin: 240; --gap: 12px; --filterW: 170px; --filterWOpen: 360px; --toggle-bg-off: #ca0000; --toggle-bg-on: #10bb2d; --toggle-nub-color: #f6f7fb; --topbarH: 64px; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji"; color: var(--text); line-height: 1.35; font-size: 14px; }
|
||||
.pexpo-core-root * { box-sizing: border-box; }
|
||||
.pexpo-core-shell { width: min(1100px, 96vw); height: min(720px, 92vh); background: linear-gradient(180deg, rgba(44,52,137,.05), transparent 42%), var(--panel); border: 1px solid var(--stroke); border-radius: calc(var(--r) + 2px); box-shadow: var(--shadow); overflow: hidden; display:flex; flex-direction:column; position: relative; isolation: isolate; }
|
||||
.pexpo-core-topbar { display:flex; align-items:flex-start; justify-content:space-between; gap: 12px; padding: 14px; border-bottom: 1px solid var(--stroke); background: linear-gradient(180deg, rgba(16,24,40,.02), rgba(16,24,40,.00)); position: relative; z-index: 3; }
|
||||
.pexpo-core-topHeight { height: 45px; }
|
||||
.pexpo-core-filterWrap { width: var(--filterW); transition: width 220ms ease; }
|
||||
.pexpo-core-filterWrap.pexpo-core-open { width: var(--filterWOpen); }
|
||||
.pexpo-core-filterBtn { width: 100%; display:flex; align-items:center; justify-content:space-between; gap: 10px; padding: 10px 10px; border-radius: 12px; border: 1px solid var(--stroke2); background: var(--panel2); color: var(--text); cursor:pointer; user-select:none; box-shadow: 0 8px 22px rgba(16,24,40,.10); }
|
||||
.pexpo-core-filterBtn .pexpo-core-left { display:flex; align-items:center; gap: 10px; min-width: 0; }
|
||||
.pexpo-core-pill { font-size: 12px; padding: 3px 8px; border: 1px solid rgba(44,52,137,.25); border-radius: 999px; color: rgba(44,52,137,.85); background: rgba(44,52,137,.06); white-space:nowrap; }
|
||||
.pexpo-core-filterBtn .pexpo-core-label { font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.pexpo-core-sortWrap { display:flex; justify-content: space-between; align-items:center; gap: 10px; padding: 10px 10px; border-radius: 12px; border: 1px solid var(--stroke2); background: var(--panel2); color: var(--text); box-shadow: 0 8px 22px rgba(16,24,40,.10); flex: 0 0 auto; }
|
||||
.pexpo-core-sortLabel { font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.pexpo-core-sortSelect { border: 1px solid rgba(16,24,40,.16); background: rgba(255,255,255,.95); color: var(--text); border-radius: 10px; padding: 5px 10px; outline: none; font: inherit; height: 35px; }
|
||||
.pexpo-core-sortSelect:focus { border-color: rgba(44,52,137,.45); box-shadow: 0 0 0 3px rgba(44,52,137,.14); }
|
||||
.pexpo-core-searchWrap { flex: 1 1 auto; display:flex; justify-content:flex-end; align-items:flex-start; }
|
||||
.pexpo-core-search { width: min(520px, 100%); display:flex; align-items:center; gap: 10px; padding: 10px 12px; border-radius: 14px; border: 1px solid var(--stroke2); background: rgba(255,255,255,.9); box-shadow: 0 8px 22px rgba(16,24,40,.10); }
|
||||
.pexpo-core-search input { width: 100%; border: none; outline: none; background: transparent; color: var(--text); font-size: 14px; }
|
||||
.pexpo-core-body { flex: 1 1 auto; padding: 14px; overflow:auto; position: relative; z-index: 1; }
|
||||
.pexpo-core-metaRow { display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 12px; color: var(--muted); font-size: 12px; }
|
||||
.pexpo-core-masonry { display:flex; gap: var(--gap); align-items:flex-start; }
|
||||
.pexpo-core-mCol { flex: 1 1 0; min-width: 0; display:flex; flex-direction:column; gap: var(--gap); }
|
||||
.pexpo-core-card { border-radius: 14px; border: 1px solid var(--stroke2); background: linear-gradient(180deg, rgba(143, 152, 255, 0.05), rgba(255,255,255,.92)); padding: 12px; box-shadow: 0 12px 26px rgba(16,24,40,.10); display:flex; flex-direction:column; gap: 8px; }
|
||||
.pexpo-core-imageWrap { width: 100%; height: auto; border-radius: 10px; overflow: hidden; }
|
||||
.pexpo-core-imageWrap img { width: 100%; height: auto; display: block; }
|
||||
.pexpo-core-cardTop { display:flex; align-items:flex-start; justify-content:space-between; gap: 10px; }
|
||||
.pexpo-core-title { font-weight: 750; letter-spacing: .15px; color: var(--text); &:hover { color: initial; } }
|
||||
.pexpo-core-tags { display: flex; flex-wrap: wrap; gap: 6px; row-gap: 6px; }
|
||||
.pexpo-core-tag { font-size: 12px; padding: 3px 8px; border-radius: 999px; border: 1px solid #333333ce; color: var(--text); background: #99999930; white-space:nowrap; }
|
||||
.pexpo-core-desc { color: rgba(16,24,40,.74); font-size: 13px; }
|
||||
.pexpo-core-foot { margin-top:auto; display:flex; align-items:center; justify-content:space-between; color: rgba(16,24,40,.60); font-size: 12px; border-top: 1px dashed rgba(16,24,40,.16); padding-top: 8px; }
|
||||
.pexpo-core-filterDrawerBackdrop { position:absolute; inset: var(--topbarH) 0 0 0; background: rgba(16,24,40,.20); backdrop-filter: blur(2px); opacity: 0; pointer-events: none; transition: opacity 180ms ease; z-index: 2; }
|
||||
.pexpo-core-filterDrawerBackdrop.pexpo-core-show { opacity: 1; pointer-events: auto; }
|
||||
.pexpo-core-filterDrawer { position:absolute; top: var(--topbarH); bottom: 0; left: 14px; width: var(--filterW); border-radius: 14px; border: 1px solid var(--stroke2); background: rgba(255,255,255,.92); overflow:hidden; box-shadow: 0 18px 45px rgba(16,24,40,.18); opacity: 0; transform: translateY(-8px); pointer-events: none; transition: opacity 200ms ease, transform 200ms ease, width 220ms ease; z-index: 3; display:flex; flex-direction:column; min-height: 0; margin: 12.5px 0 12.5px 0; }
|
||||
.pexpo-core-filterDrawer.pexpo-core-open { opacity: 1; transform: translateY(0); pointer-events: auto; width: var(--filterWOpen); }
|
||||
.pexpo-core-filterPanelHeader { display:flex; align-items:center; justify-content:space-between; padding: 10px 10px; border-bottom: 1px solid var(--stroke); color: rgba(16,24,40,.60); background: linear-gradient(180deg, rgba(44,52,137,.05), rgba(255,255,255,.0)); flex: 0 0 auto; }
|
||||
.pexpo-core-filterPanelHeader b { color: var(--text); }
|
||||
.pexpo-core-filterPanelBody { display: flex; padding: 10px; flex-direction: column; overflow:auto; min-height: 0; gap: 10px; }
|
||||
.pexpo-core-toggle { width: 100%; display: flex; justify-content: space-between; align-items: center; }
|
||||
.pexpo-core-toggle input[type="checkbox"] { display: none; }
|
||||
.pexpo-core-toggle label { position: relative; display: block; width: 50px; height: 25px; cursor: pointer; }
|
||||
.pexpo-core-toggle label::before { content: ''; display: block; width: 100%; height: 100%; border-radius: 4px; background: var(--toggle-bg-off); border: 1.5px solid color-mix(in srgb, var(--toggle-bg-off), black var(--darker)); box-sizing: border-box; transition: all 0.2s ease-in; }
|
||||
.pexpo-core-toggle label::after { content: ''; display: block; height: 21px; width: 21px; border-radius: 2px; background: var(--toggle-nub-color); position: absolute; top: 2px; left: 2px; transition: all 0.2s ease-in; box-shadow: 0 2px 5px rgba(0,0,0,0.2); }
|
||||
.pexpo-core-toggle input:checked + label::before { background: var(--toggle-bg-on); border: 1.5px solid color-mix(in srgb, var(--toggle-bg-on), black var(--darker)); }
|
||||
.pexpo-core-toggle input:checked + label::after { left: 27px; }
|
||||
.pexpo-core-shell.pexpo-core-drawerOpen .pexpo-core-body { transition: padding-left 220ms ease; }
|
||||
.pexpo-core-shell:not(.pexpo-core-drawerOpen) .pexpo-core-body { transition: padding-left 220ms ease; }
|
||||
.pexpo-core-icons-search { box-sizing: border-box; position: relative; display: block; transform: scale(var(--ggs, 1)); width: 16px; height: 16px; border: 2px solid; border-radius: 100%; margin-left: -4px; margin-top: -4px; }
|
||||
.pexpo-core-icons-search:after { content: ""; display: block; box-sizing: border-box; position: absolute; border-radius: 3px; width: 3px; height: 8px; background: currentColor; transform: rotate(-45deg); top: 10px; left: 12px; }
|
||||
.pexpo-core-icons-down { box-sizing: border-box; position: relative; display: block; transform: scale(var(--ggs, 1)); width: 22px; height: 22px; border: 2px solid transparent; border-radius: 100px; }
|
||||
.pexpo-core-icons-down:after { content: ""; display: block; box-sizing: border-box; position: absolute; width: 10px; height: 10px; border-bottom: 2px solid; border-right: 2px solid; transform: rotate(45deg); transition: 1s transform ease; left: 4px; top: 2px; }
|
||||
.pexpo-core-filterWrap.pexpo-core-open .pexpo-core-icons-down { transform: rotate(-135deg) translate(-1px,-1px); }
|
||||
.pexpo-core-icons-options { box-sizing: border-box; position: relative; display: block; transform: scale(var(--ggs, 1)); width: 10px; height: 2px; box-shadow: -3px 4px 0 0, 3px -4px 0 0; margin: 0 10px 0 10px; }
|
||||
.pexpo-core-icons-options::after, .pexpo-core-icons-options::before { content: ""; display: block; box-sizing: border-box; position: absolute; width: 8px; height: 8px; border: 2px solid; border-radius: 100%; }
|
||||
.pexpo-core-icons-options::before { top: -7px; left: -4px; }
|
||||
.pexpo-core-icons-options::after { bottom: -7px; right: -4px; }
|
||||
@media (max-width: 720px) {
|
||||
.pexpo-core-topbar { flex-direction:column; }
|
||||
.pexpo-core-searchWrap { justify-content:stretch; }
|
||||
.pexpo-core-search { width: 100%; }
|
||||
@@ -526,15 +72,11 @@
|
||||
.pexpo-core-sortWrap { width: 100%; }
|
||||
.pexpo-core-sortSelect { width: 110px; }
|
||||
.pexpo-core-searchWrap { width: 100%; }
|
||||
.pexpo-core-filterDrawer {
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
width: auto;
|
||||
}
|
||||
.pexpo-core-filterDrawer { left: 14px; right: 14px; width: auto; }
|
||||
.pexpo-core-filterDrawer.pexpo-core-open { width: auto; }
|
||||
.pexpo-core-shell.pexpo-core-drawerOpen .pexpo-core-body { padding-left: 14px; }
|
||||
}
|
||||
</style>
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<div class="pexpo-core-root pexpo-core-shell" id="shell">
|
||||
@@ -566,23 +108,26 @@
|
||||
</div>
|
||||
|
||||
<div class="pexpo-core-filterDrawerBackdrop" id="drawerBackdrop" aria-hidden="true"></div>
|
||||
<div class="pexpo-core-filterDrawer" id="filterDrawer" aria-hidden="true">
|
||||
<div class="pexpo-core-filterDrawer pexpo-core-open" id="filterDrawer" aria-hidden="true">
|
||||
<div class="pexpo-core-filterPanelHeader">
|
||||
<span><b>Filter JSON</b> (edit + apply)</span>
|
||||
<span class="pexpo-core-pill" id="applyHint">Ctrl + Enter</span>
|
||||
<span><b>Részletes keresés</b></span>
|
||||
</div>
|
||||
<div class="pexpo-core-jsonBox">
|
||||
<textarea id="filterJson" class="pexpo-core-textarea" spellcheck="false">
|
||||
{
|
||||
"category": "all",
|
||||
"sort": "relevance",
|
||||
"status": ["active", "archived"],
|
||||
"minScore": 0,
|
||||
"tags": ["ui", "mock"],
|
||||
"maxResults": 17
|
||||
}
|
||||
</textarea>
|
||||
<div class="pexpo-core-error" id="jsonError">Invalid JSON. Fix the syntax and try again.</div>
|
||||
<div class="pexpo-core-filterPanelBody">
|
||||
<div class="pexpo-core-toggle">
|
||||
<span class="toggle-text">Címke kötelezése</span>
|
||||
<div class="toggle-wrapper">
|
||||
<input type="checkbox" id="pexpo-core-tag-toggle">
|
||||
<label for="pexpo-core-tag-toggle"></label>
|
||||
</div>
|
||||
</div>
|
||||
<label for="pexpo-core-tags" style="margin-top:10px; display:block; font-weight:600;">Címkék</label>
|
||||
|
||||
<select id="pexpo-core-tags" data-placeholder="Opciók kiválasztása" multiple="multiple">
|
||||
<option value="option1">Option 1</option>
|
||||
<option value="option2">Option 2</option>
|
||||
<option value="option3">Option 3</option>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -607,10 +152,10 @@
|
||||
const filterBtn = document.getElementById("filterBtn");
|
||||
const filterDrawer = document.getElementById("filterDrawer");
|
||||
const drawerBackdrop = document.getElementById("drawerBackdrop");
|
||||
const filterFTag = document.getElementById("pexpo-core-tag-toggle");
|
||||
|
||||
const filterJson = document.getElementById("filterJson");
|
||||
const jsonError = document.getElementById("jsonError");
|
||||
const filterLabel = document.getElementById("filterLabel");
|
||||
// Element for MultiSelect
|
||||
const filterTagsSelect = document.getElementById("pexpo-core-tags");
|
||||
|
||||
const qInput = document.getElementById("q");
|
||||
const masonry = document.getElementById("masonry");
|
||||
@@ -620,9 +165,8 @@
|
||||
const layoutMeta = document.getElementById("layoutMeta");
|
||||
const sortSelect = document.getElementById("sortSelect");
|
||||
|
||||
// ---------- Mock Data Generator (Restored & Adapted) ----------
|
||||
function makeResults(n, q = "") {
|
||||
const words = [
|
||||
// ---------- Mock Data Source ----------
|
||||
const DATA_WORDS = [
|
||||
"apple","river","mountain","cloud","stone","forest","breeze","candle","mirror","shadow",
|
||||
"sunrise","sunset","ocean","island","valley","thunder","lightning","rain","snow","frost",
|
||||
"ember","flame","smoke","ash","spark","comet","star","planet","orbit","galaxy",
|
||||
@@ -651,43 +195,37 @@
|
||||
"mission","challenge","victory","legend"
|
||||
];
|
||||
|
||||
function makeResults(n, q = "") {
|
||||
const out = [];
|
||||
for (let i = 1; i <= n; i++) {
|
||||
const score = Math.round(Math.random() * 100);
|
||||
const extra = " ".repeat(Math.floor(Math.random() * 3)).replace(/ /g, "—");
|
||||
|
||||
// Generate a title containing query words if query exists (to simulate search hits)
|
||||
let title = "";
|
||||
if(q && Math.random() > 0.3) {
|
||||
title = `${q} ${words[Math.floor(Math.random() * words.length)]}`;
|
||||
title = `${q} ${DATA_WORDS[Math.floor(Math.random() * DATA_WORDS.length)]}`;
|
||||
} else {
|
||||
title = Math.random() < 0.3
|
||||
? `The ${words[Math.floor(Math.random() * words.length)]} of ${words[Math.floor(Math.random() * words.length)]}`
|
||||
: `${words[Math.floor(Math.random() * words.length)]} ${words[Math.floor(Math.random() * words.length)]}`;
|
||||
? `The ${DATA_WORDS[Math.floor(Math.random() * DATA_WORDS.length)]} of ${DATA_WORDS[Math.floor(Math.random() * DATA_WORDS.length)]}`
|
||||
: `${DATA_WORDS[Math.floor(Math.random() * DATA_WORDS.length)]} ${DATA_WORDS[Math.floor(Math.random() * DATA_WORDS.length)]}`;
|
||||
}
|
||||
|
||||
// Format for New Prod Schema
|
||||
out.push({
|
||||
id: i,
|
||||
title: title.charAt(0).toUpperCase() + title.slice(1),
|
||||
url: "#",
|
||||
// Adapting tags to be an array for the new renderer
|
||||
tag: (() => {
|
||||
const numberOfTags = Math.floor(Math.random() * 5) + 1; // 1-5 tags
|
||||
// Determine random selection
|
||||
return words.slice().sort(() => 0.5 - Math.random()).slice(0, numberOfTags);
|
||||
const numberOfTags = Math.floor(Math.random() * 4) + 1;
|
||||
return DATA_WORDS.slice().sort(() => 0.5 - Math.random()).slice(0, numberOfTags);
|
||||
})(),
|
||||
excerpt:
|
||||
"Lorem ipsum dolor sit amet consectetur adipiscing elit. Consectetur adipiscing elit quisque faucibus ex sapien vitae." +
|
||||
(score % 3 === 0 ? " Ex sapien vitae pellentesque sem placerat in id." : "") +
|
||||
(score % 5 === 0 ? " Placerat in id cursus mi pretium tellus duis. Pretium tellus duis convallis tempus leo eu aenean." : "") +
|
||||
(extra ? "" : ""),
|
||||
score, // Internal use for sorting
|
||||
score,
|
||||
image: `https://placehold.co/600x400?text=${encodeURIComponent(title)}`,
|
||||
date: new Date(Date.now() - Math.random() * 1000 * 60 * 60 * 24 * 30)
|
||||
.toISOString()
|
||||
.slice(0, 10),
|
||||
relevance: 0 // Computed locally
|
||||
date: new Date(Date.now() - Math.random() * 1000 * 60 * 60 * 24 * 30).toISOString().slice(0, 10),
|
||||
relevance: 0
|
||||
});
|
||||
}
|
||||
return out;
|
||||
@@ -695,33 +233,55 @@
|
||||
|
||||
// ---------- State ----------
|
||||
let filters = {
|
||||
q: "",
|
||||
sort: "relevance",
|
||||
status: ["active", "archived"],
|
||||
tags: ["ui", "mock"],
|
||||
maxResults: 20
|
||||
tags: [], // Array of strings
|
||||
force_tags: false, // AND vs OR logic
|
||||
maxResults: 50
|
||||
};
|
||||
|
||||
let activeSortKey = "relevance";
|
||||
let results = [];
|
||||
|
||||
// ---------- Initialize MultiSelect Library ----------
|
||||
// NOTE: This runs only if the library script is loaded
|
||||
if (typeof MultiSelect !== 'undefined') {
|
||||
|
||||
// 1. Prepare Data for Lib ({value, text})
|
||||
const multiSelectData = DATA_WORDS.sort().map(word => {
|
||||
return {
|
||||
value: word,
|
||||
text: word.charAt(0).toUpperCase() + word.slice(1)
|
||||
};
|
||||
});
|
||||
|
||||
// 2. Init
|
||||
new MultiSelect(filterTagsSelect, {
|
||||
placeholder: 'Címkék kiválasztása',
|
||||
max: 20,
|
||||
search: true,
|
||||
selectAll: true,
|
||||
onSelect: function(value, text, element) {
|
||||
if (!filters.tags.includes(value)) {
|
||||
filters.tags.push(value);
|
||||
}
|
||||
console.log('Updated filters.tags:', filters.tags);
|
||||
},
|
||||
onUnselect: function(value, text, element) {
|
||||
filters.tags = filters.tags.filter(tag => tag !== value);
|
||||
console.log('Updated filters.tags:', filters.tags);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn("MultiSelect library not found. Filters will not work without it.");
|
||||
}
|
||||
|
||||
// ---------- Sort Definitions ----------
|
||||
const SORTS = {
|
||||
relevance: {
|
||||
label: "Relevance",
|
||||
compare: (a, b) => (b.relevance - a.relevance) || (b.score - a.score)
|
||||
},
|
||||
score_desc: {
|
||||
label: "Score (high → low)",
|
||||
compare: (a, b) => (b.score - a.score)
|
||||
},
|
||||
date_desc: {
|
||||
label: "Date (new → old)",
|
||||
compare: (a, b) => new Date(b.date) - new Date(a.date)
|
||||
},
|
||||
title_asc: {
|
||||
label: "Title (A → Z)",
|
||||
compare: (a, b) => (a.title || "").localeCompare(b.title || "")
|
||||
}
|
||||
relevance: { label: "Relevance", compare: (a, b) => (b.relevance - a.relevance) || (b.score - a.score) },
|
||||
score_desc: { label: "Score (high → low)", compare: (a, b) => (b.score - a.score) },
|
||||
date_desc: { label: "Date (new → old)", compare: (a, b) => new Date(b.date) - new Date(a.date) },
|
||||
title_asc: { label: "Title (A → Z)", compare: (a, b) => (a.title || "").localeCompare(b.title || "") }
|
||||
};
|
||||
|
||||
// ---------- Helpers ----------
|
||||
@@ -731,15 +291,12 @@
|
||||
}[s]));
|
||||
}
|
||||
|
||||
// NOTE: This uses the new Production HTML structure and Classes
|
||||
function cardEl(r) {
|
||||
const div = document.createElement("div");
|
||||
div.className = "pexpo-core-card";
|
||||
div.innerHTML = `
|
||||
<div class="pexpo-core-imageWrap">
|
||||
<img src="${escapeHtml(r.image || '')}"
|
||||
alt="${escapeHtml(r.title)}"
|
||||
style="width:100%; border-radius:10px; object-fit:cover;" />
|
||||
<img src="${escapeHtml(r.image || '')}" alt="${escapeHtml(r.title)}" style="width:100%; border-radius:10px; object-fit:cover;" />
|
||||
</div>
|
||||
<div class="pexpo-core-cardTop">
|
||||
<a href="${escapeHtml(r.url)}" class="pexpo-core-title">${escapeHtml(r.title)}</a>
|
||||
@@ -747,11 +304,7 @@
|
||||
<div class="pexpo-core-desc">${escapeHtml(r.excerpt)}</div>
|
||||
<div class="pexpo-core-foot">
|
||||
<div class="pexpo-core-tags">
|
||||
${
|
||||
Array.isArray(r.tag)
|
||||
? r.tag.map(element => `<span class="pexpo-core-tag">${escapeHtml(element)}</span>`).join('')
|
||||
: ''
|
||||
}
|
||||
${Array.isArray(r.tag) ? r.tag.map(element => `<span class="pexpo-core-tag">${escapeHtml(element)}</span>`).join('') : ''}
|
||||
</div>
|
||||
<div>${escapeHtml(r.date)}</div>
|
||||
</div>
|
||||
@@ -760,8 +313,6 @@
|
||||
}
|
||||
|
||||
// ---------- Logic: Data Fetch & Relevance ----------
|
||||
|
||||
// Local client-side relevance scorer (Restored from old version)
|
||||
function computeRelevance(r, q) {
|
||||
const query = (q || "").trim().toLowerCase();
|
||||
if (!query) return r.score;
|
||||
@@ -770,7 +321,6 @@
|
||||
if (!terms.length) return r.score;
|
||||
|
||||
const title = (r.title || "").toLowerCase();
|
||||
// Check title, excerpt, and tags
|
||||
const hay = `${r.title} ${r.excerpt} ${r.tag.join(' ')}`.toLowerCase();
|
||||
|
||||
let hits = 0;
|
||||
@@ -779,8 +329,6 @@
|
||||
if (hay.includes(t)) hits++;
|
||||
if (title.includes(t)) titleHits++;
|
||||
}
|
||||
|
||||
// Weight formula
|
||||
return (hits * 30) + (titleHits * 25) + (r.score * 0.35);
|
||||
}
|
||||
|
||||
@@ -788,20 +336,26 @@
|
||||
for (const r of results) r.relevance = computeRelevance(r, q);
|
||||
}
|
||||
|
||||
// Mock Fetcher
|
||||
async function fetchData(query = "") {
|
||||
// 1. Simulate Loading
|
||||
masonry.style.opacity = "0.5";
|
||||
|
||||
// Simulate network delay (200ms) for realism
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
|
||||
try {
|
||||
// 2. Generate Data locally instead of fetching from WP
|
||||
const count = Number(filters.maxResults) || 20;
|
||||
results = makeResults(Math.min(count, 100), query);
|
||||
// 1. Generate Raw Data
|
||||
const count = Number(filters.maxResults) || 50;
|
||||
let generated = makeResults(Math.min(count, 100), query);
|
||||
|
||||
// 3. Compute relevance locally
|
||||
// 2. Apply Filters (Tags)
|
||||
if (filters.tags && filters.tags.length > 0) {
|
||||
generated = generated.filter(item => {
|
||||
// Does the item have at least one of the selected tags?
|
||||
return item.tag.some(t => filters.tags.includes(t));
|
||||
});
|
||||
}
|
||||
|
||||
results = generated;
|
||||
|
||||
// 3. Compute relevance
|
||||
recomputeRelevance(query);
|
||||
|
||||
// 4. Trigger Layout
|
||||
@@ -864,13 +418,11 @@
|
||||
for (let c = 1; c < cols; c++) {
|
||||
if (colHeights[c] < colHeights[bestCol]) bestCol = c;
|
||||
}
|
||||
|
||||
const lookN = Math.min(K, queue.length);
|
||||
let pick = 0;
|
||||
for (let i = 1; i < lookN; i++) {
|
||||
if (queue[i].h > queue[pick].h) pick = i;
|
||||
}
|
||||
|
||||
const picked = queue.splice(pick, 1)[0];
|
||||
placements.push({ item: picked, col: bestCol });
|
||||
colHeights[bestCol] += picked.h + gap();
|
||||
@@ -883,9 +435,12 @@
|
||||
count.textContent = n;
|
||||
masonry.innerHTML = "";
|
||||
|
||||
const cols = getColumnCount();
|
||||
if (n === 0) {
|
||||
masonry.innerHTML = `<div style="padding:20px; color:#666;">Nincs a szűrőnek megfelelő találat.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create Columns
|
||||
const cols = getColumnCount();
|
||||
const colEls = [];
|
||||
for (let c = 0; c < cols; c++) {
|
||||
const col = document.createElement("div");
|
||||
@@ -894,15 +449,11 @@
|
||||
colEls.push(col);
|
||||
}
|
||||
|
||||
// 1. Sort Data
|
||||
const sorter = SORTS[activeSortKey] || SORTS.relevance;
|
||||
const sorted = results.slice().sort(sorter.compare);
|
||||
|
||||
// 2. Measure & Pack
|
||||
const { measured } = measureCardHeights(cols, sorted);
|
||||
const placements = packRankAware(measured, cols, 6);
|
||||
|
||||
// 3. Render
|
||||
for (const p of placements) {
|
||||
colEls[p.col].appendChild(cardEl(p.item.r));
|
||||
}
|
||||
@@ -920,17 +471,25 @@
|
||||
sortSelect.value = activeSortKey;
|
||||
sortSelect.addEventListener("change", () => {
|
||||
activeSortKey = sortSelect.value;
|
||||
requestLayout(); // Just relayout, no need to re-fetch
|
||||
filters.sort = activeSortKey;
|
||||
requestLayout();
|
||||
});
|
||||
}
|
||||
|
||||
// Search
|
||||
qInput.addEventListener("keydown", (e) => {
|
||||
qInput.addEventListener("keyup", (e) => { // Changed to keyup for live feeling, or keep Enter
|
||||
if (e.key === "Enter") {
|
||||
fetchData(qInput.value.trim());
|
||||
filters.q = qInput.value.trim();
|
||||
fetchData(filters.q);
|
||||
}
|
||||
});
|
||||
|
||||
// Tag Toggle
|
||||
filterFTag.addEventListener("change", () => {
|
||||
filters.force_tags = filterFTag.checked;
|
||||
console.log("Force tags:", filters.force_tags);
|
||||
});
|
||||
|
||||
// Filter Drawer Logic
|
||||
function syncTopbarHeight() {
|
||||
const h = topbar.offsetHeight;
|
||||
@@ -940,15 +499,11 @@
|
||||
function setOpen(open) {
|
||||
filterWrap.classList.toggle("pexpo-core-open", open);
|
||||
filterBtn.setAttribute("aria-expanded", String(open));
|
||||
|
||||
filterDrawer.classList.toggle("pexpo-core-open", open);
|
||||
filterDrawer.setAttribute("aria-hidden", String(!open));
|
||||
|
||||
drawerBackdrop.classList.toggle("pexpo-core-show", open);
|
||||
shell.classList.toggle("pexpo-core-drawerOpen", open);
|
||||
|
||||
syncTopbarHeight();
|
||||
if (open) filterJson.focus();
|
||||
}
|
||||
|
||||
filterBtn.addEventListener("click", () => setOpen(!filterDrawer.classList.contains("pexpo-core-open")));
|
||||
@@ -957,29 +512,6 @@
|
||||
if (e.key === "Escape" && filterDrawer.classList.contains("pexpo-core-open")) setOpen(false);
|
||||
});
|
||||
|
||||
// Apply JSON Filter
|
||||
function applyJson() {
|
||||
try {
|
||||
const parsed = JSON.parse(filterJson.value);
|
||||
jsonError.classList.remove("pexpo-core-show");
|
||||
filters = parsed;
|
||||
filterLabel.textContent = (filters.category || "Custom");
|
||||
|
||||
// Trigger new search (generation) with new filters
|
||||
fetchData(qInput.value.trim());
|
||||
|
||||
} catch (err) {
|
||||
jsonError.classList.add("pexpo-core-show");
|
||||
}
|
||||
}
|
||||
|
||||
filterJson.addEventListener("keydown", (e) => {
|
||||
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
applyJson();
|
||||
}
|
||||
});
|
||||
|
||||
// Resize Observer
|
||||
const ro = new ResizeObserver(() => {
|
||||
syncTopbarHeight();
|
||||
@@ -993,6 +525,6 @@
|
||||
fetchData("");
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="js/multiselect.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user