@arcgis/map-components
Version:
ArcGIS Map Components
384 lines (383 loc) • 20.6 kB
JavaScript
/* COPYRIGHT Esri - https://js.arcgis.com/5.1/LICENSE.txt */
import { c as B } from "../../chunks/runtime.js";
import { css as H, html as o } from "lit";
import { debounce as A } from "@arcgis/toolkit/function";
import { LitElement as k, renderElement as q, safeClassMap as x } from "@arcgis/lumina";
import { templateHasKey as D, stripHTML as E } from "@arcgis/core/applications/Components/stringUtils.js";
import { renderingSanitizer as N } from "@arcgis/core/applications/Components/sanitizerUtils.js";
import { isRelatableFeatureSupportedLayer as K, findRelatedLayer as W } from "@arcgis/core/applications/Components/featureUtils.js";
import { substitute as Z, formatNumber as O } from "@arcgis/core/intl.js";
import { watch as f, on as z } from "@arcgis/core/core/reactiveUtils.js";
import { o as G } from "../../chunks/feature-utils.js";
import V from "@arcgis/core/core/Collection.js";
import M from "@arcgis/core/rest/support/RelationshipQuery.js";
import { ignoreAbortErrors as L } from "@arcgis/core/core/promiseUtils.js";
import { classes as S } from "@arcgis/toolkit/dom";
import { createRef as J, ref as U } from "lit/directives/ref.js";
import X from "@arcgis/core/popup/Feature.js";
const Y = H`:host{position:relative;flex:1 1 auto}.list{min-height:var(--calcite-spacing-xxxl)}.list-item-hidden{display:none}.feature-observer{position:relative;bottom:var(--calcite-spacing-xxl);z-index:2;text-align:center}.sticky-loading-container{display:flex;position:sticky;bottom:var(--calcite-spacing-sm);align-items:center;justify-content:center;z-index:2;margin:0;padding:0;height:var(--calcite-spacing-xxxl);pointer-events:none}`, ee = (h) => {
const e = [];
if (h.formTemplate) {
const { description: t, title: r } = h.formTemplate;
h.fields?.forEach((i) => {
const s = r && D(r, i.name), a = t && D(t, i.name);
(s || a) && e.push(i.name);
});
}
return e;
}, R = 100;
function te(h) {
return typeof h == "string" && h.trim().length > 0;
}
class re extends k {
constructor() {
super(...arguments), this._increaseFeaturePage = () => {
const { state: e, showAllEnabled: t, relatedFeatures: r, featuresPerPage: i, featurePage: s } = this;
e === "ready" && t && r.length >= i * s && this.featurePage++;
}, this._relatedFeatureIntersectionObserver = new IntersectionObserver(([e]) => {
e?.isIntersecting && this._increaseFeaturePage();
}, {
root: window.document
}), this._featurePage = 1, this._cancelQuery = () => {
const { _queryAbortController: e } = this;
e && e.abort(), this._queryAbortController = null;
}, this._cancelQueryFeatureCount = () => {
const { _queryFeatureCountAbortController: e } = this;
e && e.abort(), this._queryFeatureCountAbortController = null;
}, this._cancelQueryPage = () => {
const { _queryPageAbortController: e } = this;
e && e.abort(), this._queryPageAbortController = null;
}, this._queryController = async () => {
this._cancelQuery();
const e = new AbortController();
this._queryAbortController = e, await L(this._query()), this._queryAbortController === e && (this._queryAbortController = null);
}, this._queryFeatureCountController = async () => {
this._loaded = !1, this._cancelQueryFeatureCount();
const e = new AbortController();
this._queryFeatureCountAbortController = e, await L(this._queryFeatureCount()), this._queryFeatureCountAbortController === e && (this._queryFeatureCountAbortController = null), this._loaded = !0;
}, this._queryPageController = async () => {
const e = new AbortController();
this._queryPageAbortController = e, await L(this._queryPage()), this._queryPageAbortController === e && (this._queryPageAbortController = null);
}, this._queryDebounced = A(this._queryController, R), this._queryFeatureCountDebounced = A(this._queryFeatureCountController, R), this._queryPageDebounced = A(this._queryPageController, R), this._query = async () => {
const { _queryAbortController: e, relatedFeatures: t } = this;
!this.featureCount || this.relatedLayer?.type === "subtype-group" && !this.activeCategory || (this.relatedFeatureViewModels.destroyAll(), this.featurePage = 1, t.destroyAll(), t.addMany(this._sliceFeatures(await this._queryRelatedFeatures({ signal: e?.signal }))));
}, this._addFlowItem = (e) => {
e && this.flowItems?.push(e);
}, this._relatedFeatureIntersectionObserverNode = J(), this.relatedFeatures = new V(), this._queryAbortController = null, this._queryPageAbortController = null, this._queryFeatureCountAbortController = null, this.featureCount = 0, this._loaded = !1, this.relatedFeatureViewModels = new V(), this.autoDestroyDisabled = !1, this.featuresPerPage = 10, this.useFeatureNextFlowItem = !1, this.flowType = "feature-relationship", this.headingLevel = 2, this.hideHeading = !1, this.hideDescription = !1, this.showAllEnabled = !1;
}
static {
this.properties = { allCategories: 16, _queryAbortController: 16, _queryPageAbortController: 16, _queryFeatureCountAbortController: 16, featureCount: 16, _loaded: 16, canLoad: 16, canQuery: 16, displayShowAllButton: 16, displayListItems: 16, categories: 16, itemDescriptionFieldName: 16, relatedFeatureViewModels: 16, state: 16, autoDestroyDisabled: 5, allItemsDescription: 32, featurePage: 9, featuresPerPage: 9, flowItems: 0, useFeatureNextFlowItem: 5, flowType: 1, headingLevel: 9, messages: 0, heading: 1, description: 1, values: 0, activeCategory: 0, hideHeading: 5, hideDescription: 5, displayCount: 9, graphic: 0, layer: 0, map: 0, view: 0, spatialReference: 0, timeZone: 1, orderByFields: 0, relationshipId: 9, showAllEnabled: 5, label: 3 };
}
static {
this.styles = Y;
}
get orderByFieldsFixedCasing() {
const { orderByFields: e, relatedLayer: t } = this;
return e && t?.loaded ? e.map((r) => {
const i = r.clone();
return i.field = G(r.field, t), i;
}) : e ?? [];
}
get supportsCacheHint() {
return !!this.layer?.capabilities?.queryRelated?.supportsCacheHint;
}
get allCategoriesCount() {
return this.allCategories?.length ?? 0;
}
get objectId() {
return (this.objectIdField && this.graphic?.attributes?.[this.objectIdField]) ?? null;
}
get objectIdField() {
return this.layer?.objectIdField || null;
}
get relatedLayer() {
const { layer: e, map: t, relationship: r } = this;
if (!e?.loaded || !t || !r)
return null;
const i = e.type === "subtype-sublayer" && e.parent && K(e.parent) ? e.parent : e;
return W(t, i, r) ?? null;
}
get relatedLayerKeyField() {
const { relatedLayer: e, relationshipId: t } = this;
return e?.loaded && t != null ? e.relationships?.find((r) => r.id === t)?.keyField : null;
}
get relatedLayerKeyFields() {
const { relatedLayer: e } = this;
return e?.loaded ? e.relationships?.map((t) => t.keyField).filter((t) => t != null) ?? [] : [];
}
get relationship() {
const { relationshipId: e, layer: t } = this;
return e != null && t?.loaded ? t.relationships?.find(({ id: r }) => r === e) ?? null : null;
}
get relationshipKey() {
const { relationshipKeyField: e } = this;
return (e && this.graphic?.attributes?.[e]) ?? null;
}
get relationshipKeyField() {
return this.relationship?.keyField || null;
}
get canLoad() {
return !!this.map && this.relationshipId != null && typeof this.objectId == "number";
}
get canQuery() {
const e = this.layer?.capabilities?.queryRelated;
return !!this.relatedLayer && !!this.relationship && this.relationshipId != null && this.objectId != null && !!e?.supportsCount && e?.supportsPagination;
}
get displayShowAllButton() {
const { showAllEnabled: e, featureCount: t, displayCount: r, state: i, allCategoriesCount: s } = this, a = !!s && (s > r || r === 0), n = !!t && (t > r || r === 0);
return !e && i === "ready" && (a || n);
}
get displayListItems() {
const { relatedFeatures: e, allCategoriesCount: t } = this;
return this.displayShowAllButton || !!e.length || !!t;
}
get categories() {
const { allCategories: e } = this;
return this.showAllEnabled ? e : e?.slice(0, this.displayCount) ?? null;
}
get itemDescriptionFieldName() {
return this.orderByFieldsFixedCasing[0]?.field || null;
}
get state() {
const { _queryAbortController: e, _queryFeatureCountAbortController: t, _queryPageAbortController: r, canQuery: i, _loaded: s, canLoad: a } = this;
return t || a && !s ? "loading" : e || r ? "querying" : i ? "ready" : "disabled";
}
get allItemsDescription() {
const { messages: e, featureCount: t, allCategories: r, allCategoriesCount: i } = this;
return Z(e?.numberRecords ?? "", {
number: O(r ? i : t)
});
}
get featurePage() {
return this._featurePage;
}
set featurePage(e) {
const { featuresPerPage: t, featureCount: r } = this, i = 1, s = Math.ceil(r / t) || 1;
this._featurePage = Math.min(Math.max(e, i), s);
}
get heading() {
const { activeCategory: e } = this;
return this._heading ?? e?.name;
}
set heading(e) {
this._heading = e;
}
get displayCount() {
return this._displayCount ?? 3;
}
set displayCount(e) {
this._displayCount = Math.min(Math.max(e ?? 3, 0), 10);
}
async destroy() {
await this.manager.destroy();
}
loaded() {
this.manager.onLifecycle(() => [
f(() => [
this.displayCount,
this.graphic,
this.layer,
this.layer?.loaded,
this.map,
this.orderByFields,
this.relationshipId,
this.featuresPerPage,
this.showAllEnabled,
this.canQuery,
this.featureCount,
this.activeCategory
], () => this._queryDebounced(), { initial: !0 }),
f(() => [this.featurePage, this.showAllEnabled], () => this._queryPageDebounced()),
f(() => [this.layer, this.relationshipId, this.objectId, this.canQuery, this.activeCategory], () => this._queryFeatureCountDebounced(), { initial: !0 }),
f(() => [this.state, this.showAllEnabled, this._relatedFeatureIntersectionObserverNode], () => this._handleRelatedFeatureObserverChange()),
z(() => this.relatedFeatures, "change", () => this._setRelatedFeaturesViewModels())
]), this.manager.onDestroy(() => {
this.relatedFeatureViewModels.destroyAll(), this.relatedFeatures.destroyAll(), this._cancelQuery(), this._cancelQueryFeatureCount(), this._cancelQueryPage(), this._unobserveRelatedFeatureObserver();
});
}
async _queryFeatureCount() {
const { layer: e, relatedLayer: t } = this;
await e?.load(), await t?.load();
const { _queryFeatureCountAbortController: r, activeCategory: i, canQuery: s, objectId: a, relatedLayerKeyField: n, relationshipId: l, relationshipKey: d, supportsCacheHint: b } = this;
if (!s || !e || !t || a == null) {
this.featureCount = 0, this.allCategories = void 0;
return;
}
if (t?.type === "subtype-group" && !i) {
if (this.featureCount = 0, this.relatedFeatureViewModels.destroyAll(), this.featurePage = 1, this.relatedFeatures.destroyAll(), n && d != null) {
const { default: C } = await import("@arcgis/core/smartMapping/statistics/uniqueValues.js"), { uniqueValueInfos: w } = await C({
layer: t,
sqlWhere: `${n} = '${d}'`,
field: t.subtypeField ?? void 0,
signal: r?.signal
}), v = w.map(({ count: c, value: g }) => {
const m = t.subtypes?.find((I) => I.code === g)?.name;
return g != null && m ? {
count: c,
value: g,
name: m
} : void 0;
}).filter((c) => c != null);
this.allCategories = v;
}
return;
}
const { historicMoment: p, gdbVersion: F } = e, _ = new M({
cacheHint: b,
gdbVersion: F,
historicMoment: p,
relationshipId: l,
returnGeometry: !1,
objectIds: [a],
where: this._getRelationshipWhereClause(t)
}), y = await e.queryRelatedFeaturesCount(_, {
signal: r?.signal
});
this.allCategories = void 0, this.featureCount = y[a] || 0;
}
_getRelationshipWhereClause(e) {
const { activeCategory: t } = this, r = e.createQuery(), i = "subtypeField" in e ? e.subtypeField : void 0, s = t && i ? `${i} = ${t.value}` : void 0, a = r.where;
return a && s ? `(${a}) AND (${s})` : a ?? s;
}
_sliceFeatures(e) {
const { showAllEnabled: t, displayCount: r } = this;
return t ? e : r ? e.slice(0, r) : [];
}
async _queryPage() {
const { relatedFeatures: e, featurePage: t, showAllEnabled: r, _queryPageAbortController: i, featureCount: s } = this;
!r || t < 2 || !s || this.relatedLayer?.type === "subtype-group" && !this.activeCategory || e.addMany(await this._queryRelatedFeatures({ signal: i?.signal }));
}
async _queryRelatedFeatures(e) {
const { displayCount: t, featureCount: r, featurePage: i, featuresPerPage: s, layer: a, orderByFieldsFixedCasing: n, relatedLayer: l, relatedLayerKeyFields: d, relationshipId: b, showAllEnabled: p, supportsCacheHint: F } = this, { canQuery: _, objectId: y } = this;
if (!_ || !a || !l || y == null)
return [];
const C = p ? ((i - 1) * s + r) % r : 0, w = p ? s : t, v = l.objectIdField, c = "subtypeField" in l ? l.subtypeField : void 0, g = [
...n.map((u) => u.field),
...ee(l),
...d,
v,
c
].filter(te), m = n.map((u) => `${u.field} ${u.order}`), { historicMoment: I, gdbVersion: Q } = a, j = new M({
orderByFields: m,
start: C,
num: w,
outFields: g,
cacheHint: F,
historicMoment: I,
gdbVersion: Q,
relationshipId: b,
returnGeometry: !1,
objectIds: [y],
where: this._getRelationshipWhereClause(l)
}), $ = (await a.queryRelatedFeatures(j, {
signal: e?.signal
}))[y]?.features || [];
return l.type === "subtype-group" && c ? $.forEach((u) => {
const T = u.attributes[c], P = l.findSublayerForSubtypeCode?.(T);
u.sourceLayer = P, u.layer = P;
}) : $.forEach((u) => {
u.sourceLayer = l, u.layer = l;
}), $;
}
_selectCategory(e) {
const { flowItems: t } = this;
t && this._addRelationshipFlowItem({ showAllEnabled: !0, activeCategory: e });
}
_selectRecord(e) {
const { flowItems: t } = this;
if (!t)
return;
const r = this.useFeatureNextFlowItem ? q(o`<arcgis-feature-next .autoDestroyDisabled=${this.autoDestroyDisabled} .flowItems=${t} .flowType=${this.flowType} .graphic=${e.graphic} .map=${this.map} show-relationship-content .spatialReference=${this.spatialReference} .timeZone=${this.timeZone} .view=${this.view}></arcgis-feature-next>`) : q(o`<arcgis-feature .autoDestroyDisabled=${this.autoDestroyDisabled} .flowItems=${t} .flowType=${this.flowType} .graphic=${e.graphic} .map=${this.map} show-relationship-content .spatialReference=${this.spatialReference} .timeZone=${this.timeZone} .view=${this.view}></arcgis-feature>`);
this._addFlowItem(r);
}
_addRelationshipFlowItem({ showAllEnabled: e = !1, activeCategory: t }) {
this._addFlowItem(q(o`<arcgis-feature-relationship .autoDestroyDisabled=${this.autoDestroyDisabled} .messages=${this.messages} .activeCategory=${t} .displayCount=${this.displayCount} .flowItems=${this.flowItems} .flowType=${this.flowType} .graphic=${this.graphic} .heading=${this.heading} hide-description .showAllEnabled=${e} hide-heading .layer=${this.layer} .map=${this.map} .orderByFields=${this.orderByFields} .relationshipId=${this.relationshipId} .spatialReference=${this.spatialReference} .useFeatureNextFlowItem=${this.useFeatureNextFlowItem} .values=${this.values} .view=${this.view}></arcgis-feature-relationship>`));
}
_showAllRecords() {
const { flowItems: e } = this;
e && this._addRelationshipFlowItem({ showAllEnabled: !0 });
}
_handleRelatedFeatureObserverChange() {
this._unobserveRelatedFeatureObserver();
const { state: e, showAllEnabled: t } = this;
this._relatedFeatureIntersectionObserverNode.value && e === "ready" && t && this._relatedFeatureIntersectionObserver.observe(this._relatedFeatureIntersectionObserverNode.value);
}
_unobserveRelatedFeatureObserver() {
this._relatedFeatureIntersectionObserverNode.value && this._relatedFeatureIntersectionObserver.unobserve(this._relatedFeatureIntersectionObserverNode.value);
}
_matchesFeature(e, t) {
const r = e?.graphic?.getObjectId(), i = t?.getObjectId();
return r != null && i != null && r === i;
}
_setRelatedFeaturesViewModels() {
const { relatedFeatures: e, relatedFeatureViewModels: t, map: r, view: i, spatialReference: s, timeZone: a } = this;
e?.filter(Boolean).forEach((n) => {
t.some((d) => this._matchesFeature(d, n)) || t.add(new X({
abilities: { relationshipContent: !1 },
map: r,
view: i,
spatialReference: s,
timeZone: a,
graphic: n
}));
}), t.forEach((n) => {
e?.find((d) => this._matchesFeature(n, d)) || t.remove(n);
});
}
render() {
const { state: e } = this;
return o`<div class="root"><arcgis-feature-element-info .heading=${this.heading} .headingLevel=${this.headingLevel} .description=${this.description}></arcgis-feature-element-info>${e === "loading" ? this._renderLoading() : e === "disabled" ? this._renderRelationshipNotFound() : this._renderRelatedFeatures()}</div>`;
}
_renderStickyLoading() {
return this.state === "querying" ? o`<div class="sticky-loading-container">${this._renderLoadingIcon()}</div>` : null;
}
_renderLoadingIcon() {
return o`<calcite-loader inline label></calcite-loader>`;
}
_renderLoading() {
return o`<div class="loading-container">${this._renderLoadingIcon()}</div>`;
}
_renderShowAllIconNode() {
return o`<calcite-icon icon=list scale=s slot=content-end></calcite-icon>`;
}
_renderChevronIconNode() {
return o`<calcite-icon flip-rtl icon=chevron-right scale=s slot=content-end></calcite-icon>`;
}
_renderCategory(e) {
const { count: t, name: r } = e, i = O(t);
return o`<calcite-list-item class="list-item" .disabled=${!t} .label=${r} =${() => this._selectCategory(e)}><calcite-chip .label=${i} scale=s slot=content-end>${i}</calcite-chip>${this._renderChevronIconNode()}</calcite-list-item>`;
}
_renderRelatedFeature(e) {
const { itemDescriptionFieldName: t, values: r } = this, { formattedAttributes: i, state: s, heading: a } = e, n = {
...r,
...i?.global
}, l = (t && n?.[t]) ?? "";
return o`<calcite-list-item class=${x(S("list-item", { "list-item-hidden": s === "loading" }))} .description=${E(l.toString())} .label=${E(a)} =${() => this._selectRecord(e)}><div slot=heading><div .innerHTML=${N.sanitize(a ?? "") ?? ""}></div></div><div slot=description><div .innerHTML=${N.sanitize(l ?? "") ?? ""}></div></div>${this._renderChevronIconNode()}</calcite-list-item>`;
}
_renderShowAllListItem() {
return this.displayShowAllButton ? o`<calcite-list-item .description=${this.allItemsDescription} .label=${this.messages?.showAll} =${() => this._showAllRecords()}>${this._renderShowAllIconNode()}</calcite-list-item>` : null;
}
_renderNoRelatedFeaturesMessage() {
return o`<calcite-notice icon=information kind=brand open scale=s width=full><div slot=message>${this.messages?.noRelatedFeatures}</div></calcite-notice>`;
}
_renderFeatureObserver() {
return o`<div class="feature-observer" ${U(this._relatedFeatureIntersectionObserverNode)}></div>`;
}
_renderList() {
const { relatedFeatureViewModels: e, categories: t } = this;
return o`<calcite-list display-mode=flat .label=${this.messages?.relatedFeaturesList ?? ""}>${t?.map((r) => this._renderCategory(r)) ?? e.toArray().map((r) => this._renderRelatedFeature(r))}${this._renderShowAllListItem()}</calcite-list>`;
}
_renderRelatedFeatures() {
const { displayListItems: e } = this, { state: t } = this;
return o`<div class=${x(S("list", {
"list-querying": t === "querying"
}))}>${e ? this._renderList() : t === "ready" ? this._renderNoRelatedFeaturesMessage() : null}${this._renderStickyLoading()}${this._renderFeatureObserver()}</div>`;
}
_renderRelationshipNotFound() {
return o`<calcite-notice icon=exclamation-mark-triangle kind=danger open scale=s width=full><div slot=message>${this.messages?.relationshipNotFound}</div></calcite-notice>`;
}
}
B("arcgis-feature-relationship", re);
export {
re as ArcgisFeatureRelationship
};