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

Popup utils and display issue #475

Merged
merged 7 commits into from
Dec 22, 2023
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
5 changes: 4 additions & 1 deletion src/components/crowdsource-manager/crowdsource-manager.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@
height: calc(100% - 50px);
}


.display-none {
display: none;
}
Expand Down Expand Up @@ -175,3 +174,7 @@
visibility: hidden;
height: 0px;
}

.position-fixed {
position: fixed;
}
5 changes: 3 additions & 2 deletions src/components/crowdsource-manager/crowdsource-manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ export class CrowdsourceManager {
let sizeClass = "";
switch (layoutMode) {
case ELayoutMode.HORIZONTAL:
sizeClass = `${this._isMobile && this._hideTable ? "height-full" : panelOpen ? "height-1-2" : "height-0"} width-full position-relative`;
sizeClass = `${this._isMobile && this.hideMap ? "height-0 " : panelOpen ? "height-1-2 display-grid" : "height-0"} width-full position-relative`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I misreading this line? Expanded, I think that it is

if hideMap
    height-0
else if panelOpen
    height-1-2 display-grid
else
    height-0

And so It appears that this line is actually

if panelOpen
    height-1-2 display-grid
else
    height-0

and hideMap is irrelevant.

break;
case ELayoutMode.GRID:
sizeClass = this.classicGrid ? `${panelOpen ? "position-relative" : "position-absolute-53"} height-full width-full display-flex` :
Expand Down Expand Up @@ -741,8 +741,9 @@ export class CrowdsourceManager {
const popupNodeClass = !this._expandPopup ? "height-full" : this.mapInfos?.length === 1 || this._isMobile ? "position-absolute-0" : "position-absolute-50";
const headerClass = this._isMobile ? "display-none height-0" : "";
const headerTheme = !this._isMobile ? "calcite-mode-dark" : "calcite-mode-light";
const containerClass = this._isMobile && this._hideTable && this.hideMap ? "position-fixed width-full height-full" : this._isMobile ? "display-none height-0" : "";
return (
<div class={`${headerTheme} ${popupNodeClass}`}>
<div class={`${headerTheme} ${popupNodeClass} ${containerClass}`}>
<calcite-panel>
{
!this._isMobile ? (
Expand Down
3 changes: 1 addition & 2 deletions src/components/edit-card/edit-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,7 @@ export class EditCard {
view: this.mapView,
layerInfos,
visibleElements: {
snappingControls: false,
sketchTooltipControls: false
snappingControls: false
},
container
});
Expand Down
15 changes: 9 additions & 6 deletions src/components/info-card/info-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { Component, Element, Event, EventEmitter, Host, h, Listen, Method, Prop,
import InfoCard_T9n from "../../assets/t9n/info-card/resources.json";
import { getLocaleComponentStrings } from "../../utils/locale";
import { loadModules } from "../../utils/loadModules";
import { PopupUtils } from "../../utils/popupUtils";
import { IPopupUtils } from "../../utils/interfaces";

@Component({
tag: "info-card",
Expand Down Expand Up @@ -126,6 +128,11 @@ export class InfoCard {
*/
protected _features: __esri.Features;

/**
* IPopupUtils: When false alerts will be shown to indicate that the layer must have editing enabled for edit actions
*/
protected _popupUtils: IPopupUtils;

/**
* esri/core/reactiveUtils: https://developers.arcgis.com/javascript/latest/api-reference/esri-core-reactiveUtils.html
*/
Expand All @@ -148,6 +155,7 @@ export class InfoCard {
if (this.graphics.length > 0) {
const featureLayer = (this.graphics[0]?.layer as __esri.FeatureLayer);
this._editEnabled = featureLayer.editingEnabled && featureLayer.capabilities.operations.supportsUpdate;
this._mobileTitle = await this._popupUtils.getPopupTitle(this.graphics[0]);
this._features.open({
features: this.graphics
});
Expand Down Expand Up @@ -241,6 +249,7 @@ export class InfoCard {
async componentWillLoad(): Promise<void> {
await this._initModules();
await this._getTranslations();
this._popupUtils = new PopupUtils();
}

/**
Expand Down Expand Up @@ -435,12 +444,6 @@ export class InfoCard {
}
});

this.reactiveUtils.watch(
() => this._features.viewModel.title,
(title) => {
this._mobileTitle = title;
});

if (this.zoomAndScrollToSelected) {
this.reactiveUtils.watch(
() => this._features.selectedFeatureIndex,
Expand Down
4 changes: 4 additions & 0 deletions src/components/layer-table/layer-table.css
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,7 @@ html[dir="rtl"] .share-action {
.border-top {
border-top: 1px solid var(--calcite-color-border-2);
}

.modal {
--calcite-modal-content-padding: 0;
}
8 changes: 8 additions & 0 deletions src/components/layer-table/layer-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1666,6 +1666,7 @@ export class LayerTable {
return (
<calcite-modal
aria-labelledby="modal-title"
class="modal"
kind="brand"
onCalciteModalClose={async () => this._closeFilter()}
open={this._filterOpen}
Expand Down Expand Up @@ -1897,12 +1898,19 @@ export class LayerTable {
ids,
layer: this._layer
}
const fields = this._table.columns.toArray().reduce((prev, cur) => {
if (!(cur as any).hidden) {
prev.push((cur as any).name.toLocaleLowerCase());
}
return prev;
}, []);
void downloadUtils.downloadCSV(
null,//???
exportInfos,
false, // formatUsingLayerPopup
false, // removeDuplicates
true, // addColumnTitle
fields
);
}

Expand Down
32 changes: 20 additions & 12 deletions src/utils/downloadUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,15 @@ export async function consolidateLabels(
exportInfos: IExportInfos,
formatUsingLayerPopup = true,
includeHeaderNames = false,
isCSVExport = false
isCSVExport = false,
fields = []
): Promise<string[][]> {
const labelRequests = [];

Object.keys(exportInfos).forEach(k => {
const labelInfo: IExportInfo = exportInfos[k];
labelRequests.push(
_prepareLabels(webmap, labelInfo.layerView?.layer || labelInfo.layer, labelInfo.ids, formatUsingLayerPopup, includeHeaderNames)
_prepareLabels(webmap, labelInfo.layerView?.layer || labelInfo.layer, labelInfo.ids, formatUsingLayerPopup, includeHeaderNames, fields)
);
if (isCSVExport) {
// add the layer id as a temp value separator that we can use to split values for CSV export
Expand Down Expand Up @@ -160,9 +161,10 @@ export async function downloadCSV(
exportInfos: IExportInfos,
formatUsingLayerPopup: boolean,
removeDuplicates = false,
addColumnTitle = false
addColumnTitle = false,
fields = []
): Promise<void> {
let labels = await consolidateLabels(webmap, exportInfos, formatUsingLayerPopup, addColumnTitle, true);
let labels = await consolidateLabels(webmap, exportInfos, formatUsingLayerPopup, addColumnTitle, true, fields);
labels = removeDuplicates ? removeDuplicateLabels(labels) : labels;

const layerIds = Object.keys(exportInfos);
Expand Down Expand Up @@ -838,7 +840,8 @@ export async function _prepareLabels(
layer: __esri.FeatureLayer,
ids: number[],
formatUsingLayerPopup = true,
includeHeaderNames = false
includeHeaderNames = false,
fields = []
): Promise<string[][]> {
// Get the label formatting, if any
const labelFormatProps: ILabelFormatProps = await _getLabelFormat(webmap, layer, formatUsingLayerPopup);
Expand Down Expand Up @@ -909,7 +912,8 @@ export async function _prepareLabels(

} else {
// Get the features to export
featureSet = await queryFeaturesByID(ids, featureLayer, [], false);
const outFields = fields.length > 0 ? fields : undefined;
featureSet = await queryFeaturesByID(ids, featureLayer, [], false, undefined, outFields);
}

// Get field data types. Do we have any domain-based fields?
Expand All @@ -920,18 +924,22 @@ export async function _prepareLabels(
if (featureLayer.fields) {
featureLayer.fields.forEach(
field => {
const lowercaseFieldname = field.name.toLowerCase();
attributeOrigNames[lowercaseFieldname] = field.name;
attributeDomains[lowercaseFieldname] = field.domain;
attributeTypes[lowercaseFieldname] = field.type;
if (fields.indexOf(field.name.toLowerCase()) > -1) {
const lowercaseFieldname = field.name.toLowerCase();
attributeOrigNames[lowercaseFieldname] = field.name;
attributeDomains[lowercaseFieldname] = field.domain;
attributeTypes[lowercaseFieldname] = field.type;
}
}
);
} else {
// Feature layer is missing fields, so get info from first feature
Object.keys(featureSet[0]).forEach(
fieldName => {
const lowercaseFieldname = fieldName.toLowerCase();
attributeOrigNames[lowercaseFieldname] = fieldName;
if (fields.indexOf(fieldName.toLowerCase()) > -1) {
const lowercaseFieldname = fieldName.toLowerCase();
attributeOrigNames[lowercaseFieldname] = fieldName;
}
}
)
}
Expand Down
5 changes: 5 additions & 0 deletions src/utils/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,3 +539,8 @@ export interface ILayerAndTableIds {
layerIds: string[];
tableIds: string[];
}

export interface IPopupUtils {
arcade: typeof import("esri/arcade");
getPopupTitle(graphic: __esri.Graphic): Promise<string>;
}
104 changes: 104 additions & 0 deletions src/utils/popupUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/** @license
* Copyright 2022 Esri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { loadModules } from "./loadModules";

export class PopupUtils {

/**
* esri/arcade: https://developers.arcgis.com/javascript/latest/api-reference/esri-arcade.html
*/
arcade: typeof import("esri/arcade");

/**
* Get the popup title that honors arcade expressions
*
* @returns Promise resolving with the popup title
*
* @protected
*/
public async getPopupTitle(
graphic: __esri.Graphic
): Promise<string> {
if (!this.arcade) {
await this._initModules()
}
let attributes = {};
for (const [key, value] of Object.entries(graphic.attributes)) {
attributes = {
...attributes,
[`{${key.toUpperCase()}}`]: value
};
}
const layer = graphic.layer as __esri.FeatureLayer;
const popupTitle = this._removeTags(layer?.popupTemplate?.title as string);
if (popupTitle.includes("{expression/expr") && layer?.popupTemplate?.expressionInfos != null) {
for (let i = 0; i < layer.popupTemplate?.expressionInfos.length; i++) {
const info = layer.popupTemplate.expressionInfos[i];
const profile = {
variables: [
{
name: "$feature",
type: "feature"
}
]
} as __esri.Profile;
try {
const arcadeExecutor = await this.arcade.createArcadeExecutor(info.expression, profile);
const arcadeTitle = arcadeExecutor.execute({ $feature: graphic });
if (arcadeTitle != null || arcadeTitle !== "") {
attributes[`{expression/${info.name}}`.toUpperCase()] = arcadeTitle;
}
} catch {
continue;
}
}
}

return popupTitle?.replace(/{.*?}/g, (placeholder: string) => {
return attributes[placeholder.toUpperCase()] != null ? (attributes[placeholder.toUpperCase()] as string) : "";
});
}

/**
* Remove any tags from the title
*
* @returns title string without tags
*
* @protected
*/
protected _removeTags(str: string): string {
if (str == null || str === "") {
return "";
}
return str.toString().replace(/(<([^>]+)>)/gi, "");
}

/**
* Load esri javascript api modules
*
* @returns Promise resolving when function is done
*
* @protected
*/
protected async _initModules(): Promise<void> {
const [arcade] = await loadModules([
"esri/arcade"
]);
this.arcade = arcade;
}

}
6 changes: 5 additions & 1 deletion src/utils/queryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ export async function queryFeaturesByID(
layer: __esri.FeatureLayer,
graphics: __esri.Graphic[],
returnGeometry: boolean,
outSpatialReference?: __esri.SpatialReference
outSpatialReference?: __esri.SpatialReference,
fields?: string[]
): Promise<__esri.Graphic[]> {
const num = layer.capabilities?.query.maxRecordCount;
const start = 0;
Expand All @@ -141,6 +142,9 @@ export async function queryFeaturesByID(
if (outSpatialReference) {
q.outSpatialReference = outSpatialReference;
}
if (fields) {
q.outFields = fields;
}

const result = await layer.queryFeatures(q);

Expand Down
Loading