Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dropdown_pills added to elements; #1284

Merged
merged 6 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 105 additions & 66 deletions lib/ui/elements/dropdown.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,104 +3,143 @@ export default (params) => {
params.selectedTitles = new Set()
params.selectedOptions = new Set()

// Create array of li elements for dropdown.
const ul = params.entries.map((entry) => {

// Create li element with click event.
const li = mapp.utils.html.node`<li onclick=${(e) => {
params.pills &&= mapp.ui.elements.pills({
pills: [...params.selectedTitles],
addCallback: (val, _pills) => {
params.callback?.(null, [..._pills]);
},
removeCallback: (val, _pills) => {

// deselect value in the list
const item = ul.find(li => li.getAttribute('data-value') === val.toString());
item.classList.remove('selected');
params.selectedTitles.delete(item.title);
params.selectedOptions.delete(item.dataset.option);

// Remove pill element
//params.pills.remove(val);

params.callback?.(null, [..._pills]);
}
});

// Find the closest dropdown button.
const btn = e.target.closest('button.dropdown')
params.liOnClick ??= liOnClick

// Will collapse the dropdown if current state is 'active'.
!params.multi && btn.classList.toggle('active')
function liOnClick(e, entry) {

if (params.multi) {
// Find the closest dropdown button.
const btn = e.target.closest('button.dropdown')

// Will show select background if add to classlist.
e.target.classList.toggle('selected')
// Will collapse the dropdown if current state is 'active'.
!params.multi && btn.classList.toggle('active')

if (params.multi) {

// Will show select background if add to classlist.
e.target.classList.toggle('selected')

// Add or remove title and option value from sets.
if (e.target.classList.contains('selected')) {
params.selectedTitles.add(entry.title)
params.selectedOptions.add(entry.option)

// Add or remove title and option value from sets.
if (e.target.classList.contains('selected')) {
params.selectedTitles.add(entry.title)
params.selectedOptions.add(entry.option)
} else {
params.selectedTitles.delete(entry.title)
params.selectedOptions.delete(entry.option)
}
params.pills?.add(entry.title)

// Set btn text to reflect selection or show placeholder.
btn.querySelector('[data-id=header-span]')
.textContent = params.selectedTitles.size && Array.from(params.selectedTitles).map(v => v).join(', ')
|| params.span || params.placeholder
} else {
params.selectedTitles.delete(entry.title)
params.selectedOptions.delete(entry.option)

// Execute callback method and pass array of current selection.
params.callback?.(e, Array.from(params.selectedOptions));

return;
params.pills?.remove(entry.title);
}

if (!params.keepPlaceholder) {
if (!params.pills) {

// Dropdown is not multi
btn.querySelector('[data-id=header-span]').textContent = entry.title;
// Set btn text to reflect selection or show placeholder.
btn.querySelector('[data-id=header-span]').textContent =
params.selectedTitles.size
&& Array.from(params.selectedTitles).map(v => v).join(', ')
|| params.span || params.placeholder
}

params.callback?.(e, entry);
params.callback?.(e, [...params.selectedOptions])

return;
}

// Only applies for non multi select
if (!params.keepPlaceholder) {

// Dropdown is not multi
btn.querySelector('[data-id=header-span]').textContent = entry.title;
}

params.callback?.(e, entry);
}

// Create array of li elements for dropdown.
const ul = params.entries.map((entry) => {

}}>${entry.title}`
// Create li element with click event.
const li = mapp.utils.html.node`<li
data-value=${entry.option}
onclick=${(e) => params.liOnClick(e, entry)}>
${entry.title}`

// The entry is already selected during creation of dropdown.
if (entry.selected) {
li.classList.add('selected')
params.selectedTitles.add(entry.title)
params.selectedOptions.add(entry.option)

// create pill
params.pills && pills.add(entry.title)
}

return li
})

return mapp.utils.html.node`
<button
data-id=${params.data_id || 'dropdown'}
class="dropdown">
<div class="head" onclick=${(e) => {

const bounds = e.target.getBoundingClientRect()
params.headerOnClick ??= headerOnClick

const viewport = document.body.getBoundingClientRect()
function headerOnClick(e) {

// Set the maxHeight of the ul based on the difference between the bottom and document.body height.
e.target.nextElementSibling.style.maxHeight = `${viewport.height - bounds.bottom}px`
const bounds = e.target.getBoundingClientRect()

e.target.nextElementSibling.style.width = `${e.target.offsetWidth}px`
const viewport = document.body.getBoundingClientRect()

// Collapse dropdown element and short circuit.
if (e.target.parentElement.classList.contains('active')) {
e.target.parentElement.classList.remove('active');
return;
}
// Set the maxHeight of the ul based on the difference between the bottom and document.body height.
e.target.nextElementSibling.style.maxHeight = `${params.maxHeight || (viewport.height - bounds.bottom)}px`

// Collapse any expandxed dropdown elements in document.
document.querySelectorAll('button.dropdown')
.forEach((el) => el.classList.remove('active'));
e.target.nextElementSibling.style.width = `${e.target.offsetWidth}px`

// Expand this dropdown element.
e.target.parentElement.classList.add('active');
// Collapse dropdown element and short circuit.
if (e.target.parentElement.classList.contains('active')) {
e.target.parentElement.classList.remove('active');
return;
}

}}>
<span data-id=header-span>${
// Collapse any expandxed dropdown elements in document.
document.querySelectorAll('button.dropdown')
.forEach((el) => el.classList.remove('active'));

// join the selected titles if selectTitles set has a size.
params.selectedTitles.size && Array.from(params.selectedTitles).join(', ')
// Expand this dropdown element.
e.target.parentElement.classList.add('active');
}

// header should be the span value.
|| params.span
const headerSpan = params.selectedTitles.size
&& Array.from(params.selectedTitles).join(', ') // join the selected titles if selectTitles set has a size.
|| params.span // header should be the span value.
|| params.placeholder // use placeholder.

params.node = mapp.utils.html.node`
${params.pills?.container}
<button
data-id=${params.data_id || 'dropdown'}
class="dropdown">
<div class="head" onclick=${params.headerOnClick}>
<span data-id=header-span>${headerSpan}</span>
<div class="icon"></div>
</div>
<ul>${ul}`;

// use placeholder.
|| params.placeholder}
</span>
<div class="icon"></div>
</div>
<ul>${ul}`;
return params.node
}
19 changes: 10 additions & 9 deletions lib/ui/elements/pills.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ export default (component = {}) => {
const pill = mapp.utils.html.node`<div
class="pill"
style=${component.css_pill}
data-value=${val}
title="${val}">${val}`

// Only append the remove button to pill if configured.
if (component.removeCallback) pill.append(mapp.utils.html.node`<button
data-value="${val}"
title="${mapp.dictionary.pill_component_remove}"
data-value=${val}
title=${mapp.dictionary.pill_component_remove}
class="primary-background"
onclick=${e => {
component.remove(val);
Expand All @@ -58,15 +59,15 @@ export default (component = {}) => {


component.remove = val => {
// find the pill to remove
for(let i of component.container.children) {
if(i.title === val) {
i.remove();
break;
}
}

const pillElement = Array.from(component.container.children)
.find(child => child.getAttribute('data-value') === val.toString())

pillElement?.remove()

// remove pill from selection
component.pills.delete(val);

// execute removeCallback if defined
if (typeof component.removeCallback === 'function') component.removeCallback(val, component.pills);
}
Expand Down
4 changes: 3 additions & 1 deletion lib/ui/layers/filters.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,13 @@ async function filter_in(layer, filter) {

const chkSet = new Set(layer.filter?.current[filter.field]?.[filter.type] || []);

if (filter.dropdown) {
if (filter.dropdown || filter.dropdown_pills) {

const dropdown = mapp.ui.elements.dropdown({
pills: filter.dropdown_pills,
multi: true,
placeholder: 'Select Multiple',
maxHeight: 300,
entries: filter[filter.type].map(val => ({
title: val,
option: val,
Expand Down
Loading