Skip to content

Commit

Permalink
feature: new group options (#1910)
Browse files Browse the repository at this point in the history
* New options for groups

* Lint

* Go through subgroups as well

* Works for groups as well

* decription and abstract

Added description for layer and option to show abstract for groups on properties page as well. This way layer and groups behave quite similar.

* fix

* showAbstractInLegend option
  • Loading branch information
jokd authored Dec 4, 2023
1 parent 6ba7814 commit 39a43aa
Show file tree
Hide file tree
Showing 7 changed files with 373 additions and 208 deletions.
1 change: 1 addition & 0 deletions src/controls/legend.js
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ const Legend = function Legend(options = {}) {
},
getuseGroupIndication() { return useGroupIndication; },
getOverlaysCollapse() { return overlaysCmp.overlaysCollapse; },
getOverlays() { return overlaysCmp; },
setVisibleLayersViewActive,
addButtonToTools(button, buttonGroup) {
if (buttonGroup === 'addLayerButton') {
Expand Down
72 changes: 67 additions & 5 deletions src/controls/legend/group.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Component, Button, Collapse, CollapseHeader, dom } from '../../ui';
import GroupList from './grouplist';
import createMoreInfoButton from './moreinfobutton';
import LayerProperties from './overlayproperties';

/**
* The Group component can be a group or a subgroup,
Expand All @@ -16,13 +18,17 @@ const Group = function Group(viewer, options = {}) {
name,
parent,
abstract,
showAbstractInLegend = false,
position = 'top',
type = 'group',
autoExpand = true,
exclusive = false,
toggleAll = true,
draggable = false,
zIndexStart = 0.1
zIndexStart = 0.1,
opacityControl = false,
zoomToExtent = false,
description
} = options;

const stateCls = {
Expand All @@ -37,9 +43,11 @@ const Group = function Group(viewer, options = {}) {
let selectedItem;

const listCls = type === 'grouplayer' ? 'divider-start padding-left padding-top-small' : '';
const groupList = GroupList({ viewer, cls: listCls, abstract });
const groupList = GroupList({ viewer, cls: listCls, abstract, showAbstractInLegend });
visibleState = groupList.getVisible();

const thisGroup = viewer.getGroup(name);

const getEl = () => groupEl;

const getCheckIcon = (visible) => {
Expand Down Expand Up @@ -70,6 +78,9 @@ const Group = function Group(viewer, options = {}) {
}
}) : false;

const moreInfoButton = (opacityControl || zoomToExtent || description || (abstract && !showAbstractInLegend)) ? createMoreInfoButton({ viewer,
group: thisGroup }) : false;

const SubGroupHeader = function SubGroupHeader() {
const expandButton = Button({
cls: 'icon-small compact round',
Expand All @@ -86,6 +97,9 @@ const Group = function Group(viewer, options = {}) {
if (tickButton) {
this.addComponent(tickButton);
}
if (moreInfoButton) {
this.addComponent(moreInfoButton);
}
},
onRender() {
this.dispatch('render');
Expand All @@ -99,23 +113,33 @@ const Group = function Group(viewer, options = {}) {
});
},
render() {
return `<div class="flex row align-center padding-left text-smaller pointer collapse-header" style="width: 100%; padding-right: 1.875rem">
const padding = moreInfoButton ? '0.275rem' : '1.875rem';
return `<div class="flex row align-center padding-left text-smaller pointer collapse-header item wrap" style="width: 100%; padding-right: ${padding}">
<div id="${this.getId()}" class="flex row align-center grow">
${expandButton.render()}
<span class="grow padding-x-small" style="word-break: break-all;">${title}</span>
</div>
${tickButton ? tickButton.render() : ''}
${moreInfoButton ? moreInfoButton.render() : ''}
</div>`;
}
});
};

const GroupHeader = function GroupHeader() {
const headerComponent = CollapseHeader({
cls: 'hover padding-x padding-y-small grey-lightest border-bottom text-small',
cls: 'hover padding-x padding-y-small grey-lightest border-bottom text-small item wrap',
icon,
title
title,
style: `${moreInfoButton ? 'padding-right: 0.275rem' : ''}`
});
if (moreInfoButton) {
headerComponent.on('render', function hcRender() {
const el = document.getElementById(this.getId());
const html = moreInfoButton.render();
el.insertAdjacentHTML('beforeend', html);
});
}
return headerComponent;
};

Expand All @@ -129,6 +153,10 @@ const Group = function Group(viewer, options = {}) {
collapseX: false
});

if (moreInfoButton && type !== 'grouplayer') {
collapse.addComponent(moreInfoButton);
}

const addGroup = function addGroup(groupCmp) {
groupList.addGroup(groupCmp);
this.dispatch('add:group');
Expand Down Expand Up @@ -323,6 +351,40 @@ const Group = function Group(viewer, options = {}) {
updateGroupIndication();
});
}
if (moreInfoButton) {
groupEl.addEventListener('overlayproperties', (evt) => {
const overlaysCmp = viewer.getControlByName('legend').getOverlays();
const slidenav = overlaysCmp.slidenav;
if (evt.detail.group) {
const group = evt.detail.group;
const thisParent = this;
const label = group.labelOpacitySlider ? group.labelOpacitySlider : '';
const layerProperties = LayerProperties({
group, viewer, thisParent, labelOpacitySlider: label
});
slidenav.setSecondary(layerProperties);
slidenav.slideToSecondary();
// Include back btn and opacity slider in tab order when opened and remove when closed
const secondaryEl = document.getElementById(slidenav.getId()).querySelector('.secondary');
const backBtn = secondaryEl.getElementsByTagName('button')[0];
const opacityInput = secondaryEl.getElementsByTagName('input')[0];
backBtn.tabIndex = 0;
backBtn.focus();
if (opacityInput) {
opacityInput.tabIndex = 0;
}
backBtn.addEventListener('click', () => {
backBtn.tabIndex = -99;
if (opacityInput) {
opacityInput.tabIndex = -99;
}
}, false);
slidenav.on('slide', () => {
groupEl.classList.remove('width-100');
});
}
});
}
// only listen to tick changes for subgroups
if (type === 'grouplayer') {
groupEl.addEventListener('tick:all', (e) => {
Expand Down
5 changes: 3 additions & 2 deletions src/controls/legend/grouplist.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { Component } from '../../ui';
const LayerList = function LayerList(options, isRootGroup = false) {
const {
cls: clsSettings = '',
abstract
abstract,
showAbstractInLegend = false
} = options;

let cls = `${clsSettings} list divider-end`.trim();
Expand Down Expand Up @@ -103,7 +104,7 @@ const LayerList = function LayerList(options, isRootGroup = false) {
removeGroup,
removeOverlay,
onInit() {
if (abstract) {
if (abstract && showAbstractInLegend) {
const groupAbstract = Component({
render() {
return `<li><div id="${this.getId()}">
Expand Down
200 changes: 200 additions & 0 deletions src/controls/legend/moreinfobutton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { Component, Button, dom } from '../../ui';
import PopupMenu from '../../ui/popupmenu';
import exportToFile from '../../utils/exporttofile';

export default function createMoreInfoButton(params) {
const {
layer,
group = {},
viewer
} = params;
const popupMenuItems = [];
let moreInfoButton;
let popupMenu;
const showPopup = group.zoomToExtent && group.extent; // In case of zoomToExtent we always want to show popupmenu

const eventOverlayProps = new CustomEvent('overlayproperties', {
bubbles: true,
detail: {
layer,
group
}
});

if (layer || group.opacityControl || group.description || (group.abstract && !group.showAbstractInLegend)) {
const layerInfoMenuItem = Component({
onRender() {
const labelEl = document.getElementById(this.getId());
labelEl.addEventListener('click', (e) => {
popupMenu.setVisibility(false);
document.getElementById(moreInfoButton.getId()).dispatchEvent(eventOverlayProps);
e.preventDefault();
e.stopPropagation();
popupMenu.setVisibility(false);
});
},
render() {
const labelCls = 'text-smaller padding-x-small grow pointer no-select overflow-hidden';
return `<li id="${this.getId()}" class="${labelCls}">Visa ${layer ? 'lagerinformation' : 'gruppinformation'}</li>`;
}
});
popupMenuItems.push(layerInfoMenuItem);
}

if ((layer && layer.get('zoomToExtent')) || (group.zoomToExtent && group.extent)) {
const zoomToExtentMenuItem = Component({
onRender() {
const labelEl = document.getElementById(this.getId());
labelEl.addEventListener('click', (e) => {
if (layer) {
const extent = typeof layer.getSource !== 'undefined' && typeof layer.getSource().getExtent !== 'undefined' ? layer.getSource().getExtent() : layer.getExtent();
if (layer.getVisible()) {
viewer.getMap().getView().fit(extent, {
padding: [50, 50, 50, 50],
duration: 1000
});
e.preventDefault();
}
} else if (group.zoomToExtent) {
const extent = group.extent;
viewer.getMap().getView().fit(extent, {
padding: [50, 50, 50, 50],
duration: 1000
});
e.preventDefault();
}
e.stopPropagation();
popupMenu.setVisibility(false);
});
},
render() {
const labelCls = 'text-smaller padding-x-small grow pointer no-select overflow-hidden';
return `<li id="${this.getId()}" class="${labelCls}">Zooma till</li>`;
}
});
popupMenuItems.push(zoomToExtentMenuItem);
}

if (layer && layer.get('exportable')) {
const exportFormat = layer.get('exportFormat') || layer.get('exportformat');
let exportFormatArray = [];
if (exportFormat && typeof exportFormat === 'string') {
exportFormatArray.push(exportFormat);
} else if (exportFormat && Array.isArray(exportFormat)) {
exportFormatArray = exportFormat;
}
const formats = exportFormatArray.map(format => format.toLowerCase()).filter(format => format === 'geojson' || format === 'gpx' || format === 'kml');
if (formats.length === 0) { formats.push('geojson'); }
formats.forEach((format) => {
const exportLayerMenuItem = Component({
onRender() {
const labelEl = document.getElementById(this.getId());
labelEl.addEventListener('click', (e) => {
const features = layer.getSource().getFeatures();
exportToFile(features, format, {
featureProjection: viewer.getProjection().getCode(),
filename: layer.get('title') || 'export'
});
e.preventDefault();
e.stopPropagation();
popupMenu.setVisibility(false);
});
},
render() {
let exportLabel;
if (exportFormatArray.length > 1) {
exportLabel = `Spara lager (.${format})`;
} else { exportLabel = 'Spara lager'; }
const labelCls = 'text-smaller padding-x-small grow pointer no-select overflow-hidden';
return `<li id="${this.getId()}" class="${labelCls}">${exportLabel}</li>`;
}
});
popupMenuItems.push(exportLayerMenuItem);
});
}

if (layer && layer.get('removable')) {
const removeLayerMenuItem = Component({
onRender() {
const labelEl = document.getElementById(this.getId());
labelEl.addEventListener('click', (e) => {
const doRemove = (layer.get('promptlessRemoval') === true) || window.confirm('Vill du radera lagret?');
if (doRemove) {
viewer.getMap().removeLayer(layer);
e.preventDefault();
e.stopPropagation();
}
popupMenu.setVisibility(false);
});
},
render() {
const labelCls = 'text-smaller padding-x-small grow pointer no-select overflow-hidden';
return `<li id="${this.getId()}" class="${labelCls}">Ta bort lager</li>`;
}
});
popupMenuItems.push(removeLayerMenuItem);
}

const popupMenuList = Component({
onInit() {
this.addComponents(popupMenuItems);
},
render() {
let html = `<ul id="${this.getId()}">`;
popupMenuItems.forEach((item) => {
html += `${item.render()}`;
});
html += '</ul>';
return html;
}
});

const createPopupMenu = function createPopupMenu() {
const moreInfoButtonEl = document.getElementById(moreInfoButton.getId());
const onUnfocus = (e) => {
if (!moreInfoButtonEl.contains(e.target)) {
popupMenu.setVisibility(false);
}
};
popupMenu = PopupMenu({ onUnfocus, cls: 'overlay-popup' });
const newDiv = document.createElement('div');
newDiv.classList.add('justify-end', 'flex', 'relative', 'basis-100');
moreInfoButtonEl.insertAdjacentElement('afterend', newDiv);
newDiv.appendChild(dom.html(popupMenu.render()));
popupMenu.setContent(popupMenuList.render());
popupMenuList.dispatch('render');
popupMenu.setVisibility(true);
};

const togglePopupMenu = function togglePopupMenu() {
if (!popupMenu) {
createPopupMenu();
} else {
popupMenu.toggleVisibility();
}
};

if (popupMenuItems.length > 0) {
moreInfoButton = Button({
cls: 'round small icon-smaller no-shrink',
click() {
if (popupMenuItems.length > 1 || showPopup) {
togglePopupMenu();
} else {
document.getElementById(this.getId()).dispatchEvent(eventOverlayProps);
}
},
style: {
'align-self': 'center'
},
icon: '#ic_more_vert_24px',
ariaLabel: 'Visa lagerinfo',
tabIndex: -1
});
moreInfoButton.on('render', function onRenderButton() {
document.getElementById(this.getId()).onclick = function handleEvent(e) { e.stopPropagation(); };
});
return moreInfoButton;
}
return false;
}
Loading

0 comments on commit 39a43aa

Please sign in to comment.