quest control
This commit is contained in:
parent
7057919fd3
commit
2c871781a1
4 changed files with 429 additions and 363 deletions
|
@ -415,10 +415,9 @@ class QuestsFilter(BaseSchema):
|
|||
response={200: list[QuestSchema], **auth_responses})
|
||||
@api_etag(permissions=True, quests=True)
|
||||
def list_quests(request, filters: Query[QuestsFilter]):
|
||||
quests = get_all_quests_for_request(request)
|
||||
quest_types = frozenset(filters.quest_type.split(',')) if filters.quest_type else ()
|
||||
if quest_types:
|
||||
quests = [quest for quest in quests if quest.quest_type in quest_types]
|
||||
quest_types = filters.quest_type.split(',') if filters.quest_type else None
|
||||
quests = get_all_quests_for_request(request, quest_types)
|
||||
|
||||
if filters.level:
|
||||
quests = [quest for quest in quests if quest.level_id == filters.level]
|
||||
return quests
|
||||
|
|
|
@ -123,9 +123,16 @@ class QuestSchema(BaseSchema):
|
|||
point: PointSchema
|
||||
|
||||
|
||||
def get_all_quests_for_request(request) -> list[QuestSchema]:
|
||||
return list(chain(*(
|
||||
quest.cached_get_all_for_request(request)
|
||||
for key, quest in quest_types.items()
|
||||
if request.user.is_superuser or key in request.user_permissions.quests
|
||||
)))
|
||||
def get_all_quests_for_request(request, requested_quest_types: Optional[list[str]]) -> list[QuestSchema]:
|
||||
if requested_quest_types is None:
|
||||
return list(chain(*(
|
||||
quest.cached_get_all_for_request(request)
|
||||
for key, quest in quest_types.items()
|
||||
if request.user.is_superuser or key in request.user_permissions.quests
|
||||
)))
|
||||
else:
|
||||
return list(chain(*(
|
||||
quest.cached_get_all_for_request(request)
|
||||
for key, quest in quest_types.items()
|
||||
if key in requested_quest_types and (request.user.is_superuser or key in request.user_permissions.quests)
|
||||
)))
|
|
@ -1766,14 +1766,12 @@ blink {
|
|||
}
|
||||
}
|
||||
|
||||
.leaflet-control-key {
|
||||
.leaflet-control-key > .leaflet-control-expanding-content {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: 2rem 1fr;
|
||||
|
||||
> .leaflet-control-expanding-content {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: 2rem 1fr;
|
||||
|
||||
> .key {
|
||||
> .key {
|
||||
display: grid;
|
||||
grid-column: span 2;
|
||||
grid-template-columns: subgrid;
|
||||
|
@ -1786,63 +1784,76 @@ blink {
|
|||
box-shadow: var(--color-foreground) 0 0 calc(var(--shadow-size) / 2), inset var(--color-foreground) 0 0 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.leaflet-control-expanded > .leaflet-control-expanding-content {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
|
||||
.leaflet-control-overlays {
|
||||
.leaflet-control-overlays > .leaflet-control-expanding-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
> .leaflet-control-expanding-content {
|
||||
.overlay-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
.overlay-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&::before {
|
||||
font-family: 'Material Symbols Outlined';
|
||||
content: 'arrow_right';
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
margin-left: 3ch;
|
||||
}
|
||||
}
|
||||
|
||||
.overlay-group.expanded h4::before {
|
||||
content: 'arrow_drop_down';
|
||||
}
|
||||
|
||||
.overlay-group:not(.expanded) label {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&::before {
|
||||
font-family: 'Material Symbols Outlined';
|
||||
content: 'arrow_right';
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
margin-left: 3ch;
|
||||
}
|
||||
}
|
||||
|
||||
.overlay-group.expanded h4::before {
|
||||
content: 'arrow_drop_down';
|
||||
}
|
||||
|
||||
.overlay-group:not(.expanded) label {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.leaflet-control-quests > .leaflet-control-expanding-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.leaflet-top.leaflet-right {
|
||||
z-index: 2000;
|
||||
}
|
||||
|
|
|
@ -1623,11 +1623,9 @@ c3nav = {
|
|||
enabledIcon: c3nav._map_material_icon('grid_on'),
|
||||
disabledIcon: c3nav._map_material_icon('grid_off'),
|
||||
onEnable: () => {
|
||||
console.log('grid enable');
|
||||
c3nav._gridLayer.addTo(c3nav.map);
|
||||
},
|
||||
onDisable: () => {
|
||||
console.log('grid disable');
|
||||
c3nav._gridLayer.remove();
|
||||
},
|
||||
}).addTo(c3nav.map);
|
||||
|
@ -1694,21 +1692,21 @@ c3nav = {
|
|||
history.back(); // close the modal
|
||||
},
|
||||
|
||||
key_control: null,
|
||||
legend_control: null,
|
||||
create_key: function (theme_id) {
|
||||
c3nav_api.get(`map/legend/${theme_id}/`)
|
||||
.then(key => {
|
||||
const entries = [...key.base, ...key.groups, ...key.obstacles];
|
||||
const key_control = new KeyControl();
|
||||
const legend_control = new LegendControl();
|
||||
for (const entry of entries) {
|
||||
key_control.addKey(entry.title, entry.fill, entry.border);
|
||||
legend_control.addKey(entry.title, entry.fill, entry.border);
|
||||
}
|
||||
if (c3nav.key_control !== null) {
|
||||
c3nav.map.removeControl(c3nav.key_control);
|
||||
if (c3nav.legend_control !== null) {
|
||||
c3nav.map.removeControl(c3nav.legend_control);
|
||||
}
|
||||
if (entries.length > 0) {
|
||||
c3nav.key_control = key_control;
|
||||
key_control.addTo(c3nav.map);
|
||||
c3nav.legend_control = legend_control;
|
||||
legend_control.addTo(c3nav.map);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -2417,11 +2415,15 @@ ExpandingControl = L.Control.extend({
|
|||
e.stopPropagation();
|
||||
});
|
||||
|
||||
this.render();
|
||||
this.refresh();
|
||||
|
||||
return this._container;
|
||||
},
|
||||
|
||||
refresh: function () {
|
||||
this.render(this._content);
|
||||
},
|
||||
|
||||
expand: function () {
|
||||
if (this._pinned) return;
|
||||
this._expanded = true;
|
||||
|
@ -2445,7 +2447,7 @@ ExpandingControl = L.Control.extend({
|
|||
this.setStored('pinned', this._pinned);
|
||||
},
|
||||
|
||||
render: function () {},
|
||||
render: function (content) {},
|
||||
});
|
||||
|
||||
|
||||
|
@ -2660,91 +2662,366 @@ ThemeControl = L.Control.extend({
|
|||
},
|
||||
})
|
||||
|
||||
QuestsControl = L.Control.extend({
|
||||
QuestsControl = ExpandingControl.extend({
|
||||
options: {
|
||||
position: 'topright',
|
||||
addClasses: ''
|
||||
addClasses: 'leaflet-control-quests',
|
||||
icon: 'editor_choice',
|
||||
storageKey: 'quests',
|
||||
},
|
||||
|
||||
_questData: {},
|
||||
|
||||
onAdd: function () {
|
||||
this._container = L.DomUtil.create('div', 'leaflet-control-quests leaflet-bar ' + this.options.addClasses);
|
||||
this._button = L.DomUtil.create('a', 'material-symbols', this._container);
|
||||
$(this._button).click(this.toggleQuests).dblclick(function (e) {
|
||||
e.stopPropagation();
|
||||
this._activeQuests = new Set(this.getStored('active', []));
|
||||
this._loadedQuests = new Set();
|
||||
|
||||
ExpandingControl.prototype.onAdd.call(this);
|
||||
|
||||
this.reloadQuests().catch(err => console.error(err));
|
||||
|
||||
$(this._container).on('change', 'input[type=checkbox]', e => {
|
||||
const questName = e.target.dataset.quest;
|
||||
if (e.target.checked) {
|
||||
this.showQuest(questName);
|
||||
} else {
|
||||
this.hideQuest(questName);
|
||||
}
|
||||
});
|
||||
this._button.innerText = c3nav._map_material_icon('editor_choice');
|
||||
this._button.href = '#';
|
||||
this._button.classList.toggle('control-disabled', false);
|
||||
this.questsActive = false;
|
||||
if (localStorageWrapper.getItem('showQuests')) {
|
||||
this.showQuests();
|
||||
}
|
||||
|
||||
return this._container;
|
||||
},
|
||||
|
||||
toggleQuests: function (e) {
|
||||
if (e) e.preventDefault();
|
||||
if (c3nav._questsControl.questsActive) {
|
||||
c3nav._questsControl.hideQuests();
|
||||
} else {
|
||||
c3nav._questsControl.showQuests();
|
||||
render: function (container) {
|
||||
if (!container) return;
|
||||
const fragment = document.createDocumentFragment();
|
||||
const title = document.createElement('h4');
|
||||
title.textContent = 'Quests';
|
||||
|
||||
fragment.append(title);
|
||||
|
||||
for (const quest_name in c3nav.user_data.quests) {
|
||||
const quest = c3nav.user_data.quests[quest_name];
|
||||
|
||||
const label = document.createElement('label');
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.dataset.quest = quest_name;
|
||||
|
||||
if (this._activeQuests.has(quest_name)) {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
label.append(checkbox, quest.label);
|
||||
|
||||
fragment.append(label);
|
||||
}
|
||||
container.replaceChildren(...fragment.children);
|
||||
},
|
||||
|
||||
showQuests: function () {
|
||||
if (this.questsActive) return;
|
||||
this._button.innerText = c3nav._map_material_icon('editor_choice');
|
||||
this._button.classList.toggle('control-disabled', false);
|
||||
this.questsActive = true;
|
||||
localStorageWrapper.setItem('showQuests', '1');
|
||||
this.reloadQuests();
|
||||
showQuest: function (name) {
|
||||
if (this._activeQuests.has(name)) return;
|
||||
this._activeQuests.add(name);
|
||||
this.setStored('active', [...this._activeQuests]);
|
||||
this.reloadQuests().catch(err => console.error(err));
|
||||
},
|
||||
|
||||
reloadQuests: function() {
|
||||
if (!this.questsActive) return;
|
||||
c3nav_api.get('map/quests/')
|
||||
.then((data) => {
|
||||
for (const level_id in c3nav._questsLayers) {
|
||||
c3nav._questsLayers[level_id].clearLayers();
|
||||
}
|
||||
for (const quest of data) {
|
||||
const quest_icon = c3nav._map_material_icon(c3nav.user_data.quests[quest.quest_type].icon ?? 'editor_choice');
|
||||
L.geoJson(quest.point, {
|
||||
pointToLayer: (geom, latlng) => {
|
||||
return L.marker(latlng, {
|
||||
icon: L.divIcon({
|
||||
className: 'quest-icon',
|
||||
html: `<span>${quest_icon}</span>`,
|
||||
iconSize: [24, 24],
|
||||
iconAnchor: [12, 12],
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
.addTo(c3nav._questsLayers[quest.level_id])
|
||||
.on('click', function() {
|
||||
c3nav.open_modal();
|
||||
$.get(`/editor/quests/${quest.quest_type}/${quest.identifier}`, c3nav._modal_loaded).fail(c3nav._modal_error);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
hideQuest: function (name) {
|
||||
if (!this._activeQuests.has(name)) return;
|
||||
this._activeQuests.delete(name);
|
||||
this.setStored('active', [...this._activeQuests]);
|
||||
this.reloadQuests().catch(err => console.error(err));
|
||||
},
|
||||
|
||||
hideQuests: function () {
|
||||
if (!this.questsActive) return;
|
||||
reloadQuests: async function() {
|
||||
console.log(this);
|
||||
const activeQuests = this._activeQuests;
|
||||
const removed = this._loadedQuests.difference(activeQuests);
|
||||
const added = activeQuests.difference(this._loadedQuests);
|
||||
|
||||
if (removed.size === 0 && added.size === 0) return;
|
||||
|
||||
const questData = this._questData;
|
||||
|
||||
if (added.size > 0) {
|
||||
for(const name of added) {
|
||||
questData[name] = [];
|
||||
}
|
||||
|
||||
const added_param = [...added].join(',');
|
||||
const data = await c3nav_api.get(`map/quests/?quest_type=${added_param}`);
|
||||
for (const quest of data) {
|
||||
questData[quest.quest_type].push(quest);
|
||||
}
|
||||
}
|
||||
|
||||
for (const name of removed) {
|
||||
delete questData[name];
|
||||
}
|
||||
|
||||
this._questData = questData;
|
||||
this._loadedQuests = new Set([...activeQuests]);
|
||||
|
||||
for (const level_id in c3nav._questsLayers) {
|
||||
c3nav._questsLayers[level_id].clearLayers();
|
||||
}
|
||||
this._button.innerText = c3nav._map_material_icon('editor_choice');
|
||||
this._button.classList.toggle('control-disabled', true);
|
||||
this.questsActive = false;
|
||||
localStorageWrapper.removeItem('showQuests');
|
||||
}
|
||||
|
||||
for (const quest_type in this._questData) {
|
||||
const quests = this._questData[quest_type];
|
||||
const quest_icon = c3nav._map_material_icon(c3nav.user_data.quests[quest_type].icon ?? 'editor_choice');
|
||||
|
||||
for (const quest of quests) {
|
||||
L.geoJson(quest.point, {
|
||||
pointToLayer: (geom, latlng) => {
|
||||
return L.marker(latlng, {
|
||||
icon: L.divIcon({
|
||||
className: 'quest-icon',
|
||||
html: `<span>${quest_icon}</span>`,
|
||||
iconSize: [24, 24],
|
||||
iconAnchor: [12, 12],
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
.addTo(c3nav._questsLayers[quest.level_id])
|
||||
.on('click', function () {
|
||||
c3nav.open_modal();
|
||||
$.get(`/editor/quests/${quest_type}/${quest.identifier}`, c3nav._modal_loaded).fail(c3nav._modal_error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
LegendControl = ExpandingControl.extend({
|
||||
options: {
|
||||
position: 'topright',
|
||||
addClasses: 'leaflet-control-key',
|
||||
icon: 'legend_toggle',
|
||||
storageKey: 'legend',
|
||||
},
|
||||
_keys: [],
|
||||
|
||||
addKey: function (name, background, border) {
|
||||
this._keys.push({
|
||||
name,
|
||||
background,
|
||||
border,
|
||||
});
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
render: function (container) {
|
||||
if (!container) return;
|
||||
const fragment = document.createDocumentFragment();
|
||||
for (const key of this._keys) {
|
||||
const key_container = document.createElement('div');
|
||||
key_container.classList.add('key');
|
||||
const color = document.createElement('div');
|
||||
color.classList.add('key-color');
|
||||
if (key.background !== null) {
|
||||
color.style.backgroundColor = key.background;
|
||||
}
|
||||
if (key.border !== null) {
|
||||
color.style.borderColor = key.border;
|
||||
}
|
||||
|
||||
const name = document.createElement('div');
|
||||
name.innerText = key.name;
|
||||
key_container.append(color, name);
|
||||
fragment.append(key_container);
|
||||
}
|
||||
container.replaceChildren(...fragment.children);
|
||||
},
|
||||
});
|
||||
|
||||
OverlayControl = ExpandingControl.extend({
|
||||
options: {
|
||||
position: 'topright',
|
||||
addClasses: 'leaflet-control-overlays',
|
||||
icon: 'stacks',
|
||||
storageKey: 'overlays',
|
||||
levels: {}
|
||||
},
|
||||
|
||||
_overlays: {},
|
||||
_ungrouped: [],
|
||||
_groups: {},
|
||||
|
||||
initialize: function ({levels, ...config}) {
|
||||
this.config = config;
|
||||
this._levels = levels;
|
||||
},
|
||||
|
||||
onAdd: function () {
|
||||
|
||||
const initialActiveOverlays = this.getStored('active', []);
|
||||
const initialCollapsedGroups = this.getStored('collapsed', []);
|
||||
|
||||
for (const overlay of initialActiveOverlays) {
|
||||
if (overlay in this._overlays) {
|
||||
this._overlays[overlay].visible = true;
|
||||
this._overlays[overlay].enable(this._levels);
|
||||
}
|
||||
}
|
||||
|
||||
for (const group of initialCollapsedGroups) {
|
||||
if (group in this._groups) {
|
||||
this._groups[group].expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
ExpandingControl.prototype.onAdd.call(this);
|
||||
|
||||
this.refresh();
|
||||
|
||||
$(this._container).on('change', 'input[type=checkbox]', e => {
|
||||
this._overlays[e.target.dataset.id].visible = e.target.checked;
|
||||
this.updateOverlay(e.target.dataset.id);
|
||||
});
|
||||
$(this._container).on('click', '.content h4', e => {
|
||||
this.toggleGroup(e.target.parentElement.dataset.group);
|
||||
});
|
||||
return this._container;
|
||||
},
|
||||
|
||||
addOverlay: function (overlay) {
|
||||
this._overlays[overlay.id] = overlay;
|
||||
if (overlay.group == null) {
|
||||
this._ungrouped.push(overlay);
|
||||
} else {
|
||||
if (overlay.group in this._groups) {
|
||||
this._groups[overlay.group].overlays.push(overlay);
|
||||
} else {
|
||||
this._groups[overlay.group] = {
|
||||
expanded: this._initialCollapsedGroups === null || !this._initialCollapsedGroups.includes(overlay.group),
|
||||
overlays: [overlay],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
updateOverlay: function (id) {
|
||||
const overlay = this._overlays[id];
|
||||
if (overlay.visible) {
|
||||
overlay.enable(this._levels);
|
||||
} else {
|
||||
overlay.disable(this._levels);
|
||||
}
|
||||
const activeOverlays = Object.keys(this._overlays).filter(k => this._overlays[k].visible);
|
||||
this.setStored('active', activeOverlays);
|
||||
},
|
||||
|
||||
render: function (container) {
|
||||
if (!container) return;
|
||||
|
||||
const ungrouped = document.createDocumentFragment();
|
||||
const groups = document.createDocumentFragment();
|
||||
|
||||
const render_overlays = (overlays, container) => {
|
||||
for (const overlay of overlays) {
|
||||
const label = document.createElement('label');
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.dataset.id = overlay.id;
|
||||
if (overlay.visible) {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
label.append(checkbox, overlay.title);
|
||||
container.append(label);
|
||||
}
|
||||
};
|
||||
|
||||
render_overlays(this._ungrouped, ungrouped);
|
||||
|
||||
for (const group in this._groups) {
|
||||
const group_container = document.createElement('div');
|
||||
group_container.classList.add('overlay-group');
|
||||
if (this._groups[group].expanded) {
|
||||
group_container.classList.add('expanded');
|
||||
}
|
||||
this._groups[group].el = group_container;
|
||||
group_container.dataset.group = group;
|
||||
const title = document.createElement('h4');
|
||||
title.innerText = group;
|
||||
group_container.append(title);
|
||||
render_overlays(this._groups[group].overlays, group_container);
|
||||
groups.append(group_container);
|
||||
}
|
||||
container.replaceChildren(...ungrouped.children, ...groups.children);
|
||||
},
|
||||
|
||||
toggleGroup: function (name) {
|
||||
const group = this._groups[name];
|
||||
group.expanded = !group.expanded;
|
||||
group.el.classList.toggle('expanded', group.expanded);
|
||||
const collapsedGroups = Object.keys(this._groups).filter(k => !this._groups[k].expanded);
|
||||
this.setStored('collapsed', collapsedGroups);
|
||||
},
|
||||
});
|
||||
|
||||
var SvgIcon = L.Icon.extend({
|
||||
options: {
|
||||
// @section
|
||||
// @aka DivIcon options
|
||||
iconSize: [12, 12], // also can be set through CSS
|
||||
|
||||
// iconAnchor: (Point),
|
||||
// popupAnchor: (Point),
|
||||
|
||||
// @option html: String|SVGElement = ''
|
||||
// Custom HTML code to put inside the div element, empty by default. Alternatively,
|
||||
// an instance of `SVGElement`.
|
||||
iconSvg: null,
|
||||
shadowSvg: null,
|
||||
|
||||
// @option bgPos: Point = [0, 0]
|
||||
// Optional relative position of the background, in pixels
|
||||
bgPos: null,
|
||||
|
||||
className: 'leaflet-svg-icon'
|
||||
},
|
||||
|
||||
// @method createIcon(oldIcon?: HTMLElement): HTMLElement
|
||||
// Called internally when the icon has to be shown, returns a `<img>` HTML element
|
||||
// styled according to the options.
|
||||
createIcon: function (oldIcon) {
|
||||
return this._createIcon('icon', oldIcon);
|
||||
},
|
||||
|
||||
// @method createShadow(oldIcon?: HTMLElement): HTMLElement
|
||||
// As `createIcon`, but for the shadow beneath it.
|
||||
createShadow: function (oldIcon) {
|
||||
return this._createIcon('shadow', oldIcon);
|
||||
},
|
||||
|
||||
_createIcon: function (name, oldIcon) {
|
||||
const src = this.options[`${name}Svg`];
|
||||
|
||||
if (!src) {
|
||||
if (name === 'icon') {
|
||||
throw new Error('iconSvg not set in Icon options (see the docs).');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let svgEl;
|
||||
if (src instanceof SVGElement) {
|
||||
svgEl = src;
|
||||
} else {
|
||||
svgEl = (new DOMParser()).parseFromString(src, 'image/svg+xml').documentElement;
|
||||
}
|
||||
|
||||
this._setIconStyles(svgEl, name);
|
||||
|
||||
return svgEl;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
L.SquareGridLayer = L.Layer.extend({
|
||||
initialize: function (config) {
|
||||
this.config = config;
|
||||
|
@ -2844,234 +3121,6 @@ L.SquareGridLayer = L.Layer.extend({
|
|||
}
|
||||
});
|
||||
|
||||
KeyControl = ExpandingControl.extend({
|
||||
options: {
|
||||
position: 'topright',
|
||||
addClasses: 'leaflet-control-key',
|
||||
icon: 'legend_toggle',
|
||||
storageKey: 'key',
|
||||
},
|
||||
_keys: [],
|
||||
|
||||
addKey: function (name, background, border) {
|
||||
this._keys.push({
|
||||
name,
|
||||
background,
|
||||
border,
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
if (!this._content) return;
|
||||
const fragment = document.createDocumentFragment();
|
||||
for (const key of this._keys) {
|
||||
const key_container = document.createElement('div');
|
||||
key_container.classList.add('key');
|
||||
const color = document.createElement('div');
|
||||
color.classList.add('key-color');
|
||||
if (key.background !== null) {
|
||||
color.style.backgroundColor = key.background;
|
||||
}
|
||||
if (key.border !== null) {
|
||||
color.style.borderColor = key.border;
|
||||
}
|
||||
|
||||
const name = document.createElement('div');
|
||||
name.innerText = key.name;
|
||||
key_container.append(color, name);
|
||||
fragment.append(key_container);
|
||||
}
|
||||
this._content.replaceChildren(...fragment.children);
|
||||
},
|
||||
});
|
||||
|
||||
OverlayControl = ExpandingControl.extend({
|
||||
options: {
|
||||
position: 'topright',
|
||||
addClasses: 'leaflet-control-overlays',
|
||||
icon: 'stacks',
|
||||
storageKey: 'overlays',
|
||||
levels: {}
|
||||
},
|
||||
|
||||
_overlays: {},
|
||||
_ungrouped: [],
|
||||
_groups: {},
|
||||
|
||||
initialize: function ({levels, ...config}) {
|
||||
this.config = config;
|
||||
this._levels = levels;
|
||||
},
|
||||
|
||||
onAdd: function () {
|
||||
|
||||
const initialActiveOverlays = this.getStored('active', []);
|
||||
const initialCollapsedGroups = this.getStored('collapsed', []);
|
||||
|
||||
for (const overlay of initialActiveOverlays) {
|
||||
if (overlay in this._overlays) {
|
||||
this._overlays[overlay].visible = true;
|
||||
this._overlays[overlay].enable(this._levels);
|
||||
}
|
||||
}
|
||||
|
||||
for (const group of initialCollapsedGroups) {
|
||||
if (group in this._groups) {
|
||||
this._groups[group].expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
ExpandingControl.prototype.onAdd.call(this);
|
||||
|
||||
this.render();
|
||||
|
||||
$(this._container).on('change', 'input[type=checkbox]', e => {
|
||||
this._overlays[e.target.dataset.id].visible = e.target.checked;
|
||||
this.updateOverlay(e.target.dataset.id);
|
||||
});
|
||||
$(this._container).on('click', '.content h4', e => {
|
||||
this.toggleGroup(e.target.parentElement.dataset.group);
|
||||
});
|
||||
return this._container;
|
||||
},
|
||||
|
||||
addOverlay: function (overlay) {
|
||||
this._overlays[overlay.id] = overlay;
|
||||
if (overlay.group == null) {
|
||||
this._ungrouped.push(overlay);
|
||||
} else {
|
||||
if (overlay.group in this._groups) {
|
||||
this._groups[overlay.group].overlays.push(overlay);
|
||||
} else {
|
||||
this._groups[overlay.group] = {
|
||||
expanded: this._initialCollapsedGroups === null || !this._initialCollapsedGroups.includes(overlay.group),
|
||||
overlays: [overlay],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this.render();
|
||||
},
|
||||
|
||||
updateOverlay: function (id) {
|
||||
const overlay = this._overlays[id];
|
||||
if (overlay.visible) {
|
||||
overlay.enable(this._levels);
|
||||
} else {
|
||||
overlay.disable(this._levels);
|
||||
}
|
||||
const activeOverlays = Object.keys(this._overlays).filter(k => this._overlays[k].visible);
|
||||
this.setStored('active', activeOverlays);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
if (!this._content) return;
|
||||
|
||||
const ungrouped = document.createDocumentFragment();
|
||||
const groups = document.createDocumentFragment();
|
||||
|
||||
|
||||
const render_overlays = (overlays, container) => {
|
||||
for (const overlay of overlays) {
|
||||
const label = document.createElement('label');
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.dataset.id = overlay.id;
|
||||
if (overlay.visible) {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
label.append(checkbox, overlay.title);
|
||||
container.append(label);
|
||||
}
|
||||
};
|
||||
|
||||
render_overlays(this._ungrouped, ungrouped);
|
||||
|
||||
|
||||
for (const group in this._groups) {
|
||||
const group_container = document.createElement('div');
|
||||
group_container.classList.add('overlay-group');
|
||||
if (this._groups[group].expanded) {
|
||||
group_container.classList.add('expanded');
|
||||
}
|
||||
this._groups[group].el = group_container;
|
||||
group_container.dataset.group = group;
|
||||
const title = document.createElement('h4');
|
||||
title.innerText = group;
|
||||
group_container.append(title);
|
||||
render_overlays(this._groups[group].overlays, group_container);
|
||||
groups.append(group_container);
|
||||
}
|
||||
this._content.replaceChildren(...ungrouped.children, ...groups.children);
|
||||
},
|
||||
|
||||
toggleGroup: function (name) {
|
||||
const group = this._groups[name];
|
||||
group.expanded = !group.expanded;
|
||||
group.el.classList.toggle('expanded', group.expanded);
|
||||
const collapsedGroups = Object.keys(this._groups).filter(k => !this._groups[k].expanded);
|
||||
this.setStored('collapsed', collapsedGroups);
|
||||
},
|
||||
});
|
||||
|
||||
var SvgIcon = L.Icon.extend({
|
||||
options: {
|
||||
// @section
|
||||
// @aka DivIcon options
|
||||
iconSize: [12, 12], // also can be set through CSS
|
||||
|
||||
// iconAnchor: (Point),
|
||||
// popupAnchor: (Point),
|
||||
|
||||
// @option html: String|SVGElement = ''
|
||||
// Custom HTML code to put inside the div element, empty by default. Alternatively,
|
||||
// an instance of `SVGElement`.
|
||||
iconSvg: null,
|
||||
shadowSvg: null,
|
||||
|
||||
// @option bgPos: Point = [0, 0]
|
||||
// Optional relative position of the background, in pixels
|
||||
bgPos: null,
|
||||
|
||||
className: 'leaflet-svg-icon'
|
||||
},
|
||||
|
||||
// @method createIcon(oldIcon?: HTMLElement): HTMLElement
|
||||
// Called internally when the icon has to be shown, returns a `<img>` HTML element
|
||||
// styled according to the options.
|
||||
createIcon: function (oldIcon) {
|
||||
return this._createIcon('icon', oldIcon);
|
||||
},
|
||||
|
||||
// @method createShadow(oldIcon?: HTMLElement): HTMLElement
|
||||
// As `createIcon`, but for the shadow beneath it.
|
||||
createShadow: function (oldIcon) {
|
||||
return this._createIcon('shadow', oldIcon);
|
||||
},
|
||||
|
||||
_createIcon: function (name, oldIcon) {
|
||||
const src = this.options[`${name}Svg`];
|
||||
|
||||
if (!src) {
|
||||
if (name === 'icon') {
|
||||
throw new Error('iconSvg not set in Icon options (see the docs).');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let svgEl;
|
||||
if (src instanceof SVGElement) {
|
||||
svgEl = src;
|
||||
} else {
|
||||
svgEl = (new DOMParser()).parseFromString(src, 'image/svg+xml').documentElement;
|
||||
}
|
||||
|
||||
this._setIconStyles(svgEl, name);
|
||||
|
||||
return svgEl;
|
||||
},
|
||||
});
|
||||
|
||||
class DataOverlay {
|
||||
levels = null;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue