UNPKG

@arcgis/map-components

Version:
384 lines (383 loc) • 20.6 kB
/* 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} @calciteListItemSelect=${() => 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)} @calciteListItemSelect=${() => 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} @calciteListItemSelect=${() => 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 };