UNPKG

@arcgis/map-components

Version:
560 lines (559 loc) • 23.5 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.1/LICENSE.txt */ import { c as B } from "../../chunks/runtime.js"; import { css as W, html as _ } from "lit"; import { ref as j } from "lit/directives/ref.js"; import { keyed as G } from "lit/directives/keyed.js"; import { useDirection as q, useMedia as K } from "@arcgis/lumina/controllers"; import { getColorsFromRenderer as J } from "@arcgis/core/renderers/support/utils.js"; import { LitElement as Q, nothing as y } from "@arcgis/lumina"; import { substitute as Z } from "@arcgis/core/intl.js"; import { watch as ee } from "@arcgis/core/core/reactiveUtils.js"; import te from "@arcgis/core/popup/FieldInfo.js"; import V from "@arcgis/core/popup/content/support/ChartMediaInfoValueSeries.js"; import { unsafeGetCalciteModeName as ie } from "@arcgis/toolkit/dom"; import { renderingSanitizer as N } from "@arcgis/core/applications/Components/sanitizerUtils.js"; import { d as A, s as R, l as z, m as X, n as ae, o as E, i as re, p as ne, h as Y, f as se } from "../../chunks/feature-utils.js"; const oe = W`.container{display:flex;flex-flow:row wrap;align-items:center;margin-top:var(--calcite-spacing-xs);width:100%;min-height:150px}.container .pagination-button{display:flex;align-items:center;justify-content:center;border:none;background:transparent;cursor:pointer;padding-block:var(--calcite-spacing-xs);padding-inline:var(--calcite-spacing-xs);color:var(--calcite-color-text-3)}.container .pagination-button:hover,.container .pagination-button:focus{background-color:var(--calcite-color-foreground-3);color:var(--calcite-color-text-1);fill:var(--calcite-color-text-1)}.item-title{margin:0;font-size:var(--calcite-font-size-md);line-height:var(--calcite-font-line-height-relative-snug)}.item-caption{padding-block-start:var(--calcite-spacing-xxs);font-size:var(--calcite-font-size-sm);line-height:var(--calcite-font-line-height-relative-snug)}.item{display:flex;align-items:flex-start;justify-content:center;margin-block:var(--calcite-spacing-xs);width:100%;height:auto}.item-navigation{display:flex;flex:0 1 100%;align-items:flex-start;justify-content:space-between;margin-block-end:var(--calcite-spacing-xs)}.item-text{margin-inline-end:var(--calcite-spacing-xs)}.pagination{display:flex;flex:0 0 auto;align-items:center;overflow:hidden}.pagination:only-child{margin-inline-start:auto}.pagination-text{padding-inline:var(--calcite-spacing-xxs);color:var(--calcite-color-text-3);font-size:var(--calcite-font-size-sm);line-height:var(--calcite-font-line-height-relative-normal)}.item-container{flex:0 1 auto;width:100%}.item-container img{max-width:100%}.item-container img[src$=".SVG"],.item-container img[src$=".svg"]{width:100%}.chart{width:100%;height:155px}`; function $(S) { if (!A(S)) return null; const [e, t] = S.split("/").slice(1); return { layerId: e, fieldName: t }; } const k = 15, C = "category", w = "value", le = "rgba(50, 50, 50, 1)", ce = 250, de = 500, ue = 200; class he extends Q { constructor() { super(...arguments), this._direction = q(), this._prefersReducedMotion = K("(prefers-reduced-motion)"), this.activeMediaInfoIndex = 0, this.chartAnimationDisabled = !1, this.headingLevel = 2; } static { this.properties = { _refreshIntervalInfo: 16, messages: 0, values: 0, expressionValues: 0, activeMediaInfoIndex: 9, chartAnimationDisabled: 5, activeMediaInfo: 32, formattedMediaInfos: 32, description: 1, fieldInfoMap: 0, layer: 0, mediaInfos: 0, popupTemplate: 0, relatedInfos: 0, formattedValues: 0, heading: 1, graphic: 0, formattedMediaInfoCount: 32, headingLevel: 9 }; } static { this.styles = oe; } get activeMediaInfo() { return this.formattedMediaInfos[this.activeMediaInfoIndex] || null; } get formattedMediaInfos() { return this._formatMediaInfos() || []; } get formattedMediaInfoCount() { return this.formattedMediaInfos.length; } next() { this._pageContentElementMedia(1); } previous() { this._pageContentElementMedia(-1); } setActiveMedia(e) { this._setContentElementMedia(e); } loaded() { this.manager.onLifecycle(() => [ ee(() => [this.activeMediaInfo, this.activeMediaInfoIndex], () => this._setupMediaRefreshTimer(), { initial: !0 }), { remove: () => this._clearMediaRefreshTimer() } ]); } _setContentElementMedia(e) { const { formattedMediaInfoCount: t } = this, i = (e + t) % t; this.activeMediaInfoIndex = i; } _pageContentElementMedia(e) { const { activeMediaInfoIndex: t } = this, i = t + e; this._setContentElementMedia(i); } _formatMediaInfos() { const { mediaInfos: e, layer: t } = this, i = this.values ?? {}, a = this.formattedValues ?? {}, n = this.fieldInfoMap ?? /* @__PURE__ */ new Map(), s = this.expressionValues ?? {}; return e?.map((o) => { const r = o?.clone(); if (!r) return null; if (r.title = R({ values: i, fieldInfoMap: n, globalValues: a, layer: t, text: r.title }), r.caption = R({ values: i, fieldInfoMap: n, globalValues: a, layer: t, text: r.caption }), r.altText = R({ values: i, fieldInfoMap: n, globalValues: a, layer: t, text: r.altText }), r.type === "image") { if (!r.value) return null; const c = r.value; return this._setImageValue({ value: c, formattedValues: a, layer: t }), c.sourceURL ? r : void 0; } if (r.type === "pie-chart" || r.type === "line-chart" || r.type === "column-chart" || r.type === "bar-chart") { if (!r.value) return null; const c = r.value; return this._setChartValue({ value: c, chartType: r.type, values: i, expressionValues: s, formattedValues: a, layer: t }), r; } return null; }).filter((o) => o != null) ?? []; } _setImageValue(e) { const t = this.fieldInfoMap ?? /* @__PURE__ */ new Map(), { value: i, formattedValues: a, layer: n } = e, { linkURL: s, sourceURL: o } = i; if (o) { const r = z(o, n); i.sourceURL = X({ formattedValues: a, template: r, fieldInfoMap: t }); } if (s) { const r = z(s, n); i.linkURL = X({ formattedValues: a, template: r, fieldInfoMap: t }); } } _setChartValue(e) { const { value: t, values: i, expressionValues: a, formattedValues: n, chartType: s, layer: o } = e, { popupTemplate: r, relatedInfos: c } = this, { fields: m, normalizeField: d } = t, l = o; if (t.fields = ae(m, l), d && (t.normalizeField = E(d, l)), !m.some((f) => !!(n[f] != null || A(f) && c?.size))) return; const u = r?.fieldInfos ?? []; t.series = [], m.forEach((f, h) => { const g = t.colors?.[h]; if (A(f)) { t.series = [ ...t.series, ...this._getRelatedChartInfos({ fieldInfos: u, fieldName: f, formattedValues: n, expressionValues: a, chartType: s, value: t, color: g }) ]; return; } const x = this._getChartOption({ value: t, values: i, chartType: s, formattedValues: n, expressionValues: a, fieldName: f, fieldInfos: u, color: g }); t.series.push(x); }); } _getRelatedChartInfos(e) { const { fieldInfos: t, fieldName: i, formattedValues: a, chartType: n, value: s, color: o, expressionValues: r } = e, c = [], m = $(i), d = m && this.relatedInfos?.get(m.layerId); if (!d) return c; const { relatedFeatures: l, relation: p } = d; if (!p || !l) return c; const { cardinality: u } = p; return l.forEach((h) => { const g = h.attributes; g && Object.keys(g).forEach((x) => { x === m.fieldName && c.push(this._getChartOption({ value: s, values: g, formattedValues: a, expressionValues: r, fieldName: i, chartType: n, relatedFieldName: x, hasMultipleRelatedFeatures: l?.length > 1, fieldInfos: t, color: o })); }); }), u && (u === "one-to-many" || u === "many-to-many") ? c : [c[0]]; } _getTooltip({ label: e, value: t, chartType: i }) { return i === "pie-chart" ? `${e}` : `${e}: ${t}`; } _getChartOption(e) { const { value: t, values: i, formattedValues: a, expressionValues: n, fieldName: s, relatedFieldName: o, fieldInfos: r, chartType: c, hasMultipleRelatedFeatures: m, color: d } = e, { layer: l, graphic: p } = this, u = this.fieldInfoMap ?? /* @__PURE__ */ new Map(), { normalizeField: f, tooltipField: h } = t, g = f ? A(f) ? i[$(f).fieldName] : i[f] : null, x = re(s) && n?.[s] !== void 0 ? n[s] : o && i[o] !== void 0 ? i[o] : i[s] !== void 0 ? i[s] : a[s], I = x === void 0 ? null : x && g && typeof x == "number" && typeof g == "number" ? x / g : x, v = ne(r, s), M = E(s, l), b = (h && a[h] !== void 0 ? a[h] : Y({ fieldInfo: v || new te({ fieldName: M }), expressionInfos: this.popupTemplate?.expressionInfos, graphic: p, layer: l })) ?? "", P = a[M] ?? "", L = I != null ? typeof I == "number" ? I : Number(I) : null; if (A(s)) { const T = u.get(s.toLowerCase()), U = h ? u.get(h.toLowerCase()) : null, H = T?.fieldName ?? s, F = m && h ? $(h).fieldName : U?.fieldName ?? h, D = (m && F ? i[F] : a[F] ?? (T && Y({ fieldInfo: T, layer: l, graphic: p })) ?? T?.fieldName ?? o) ?? "", O = (m && o ? i[o] : a[H]) ?? ""; return new V({ fieldName: s, tooltip: this._getTooltip({ label: D, value: O, chartType: c }), color: d, value: L }); } return new V({ fieldName: s, tooltip: this._getTooltip({ label: b, value: P, chartType: c }), color: d, value: L }); } _getRenderer() { const e = this.layer; return this.graphic?.isAggregate && e?.featureReduction && "renderer" in e.featureReduction ? e.featureReduction.renderer : e?.renderer; } async _getSeriesColors(e) { const { color: t } = await import("@amcharts/amcharts5/index.js"), i = /* @__PURE__ */ new Map(); return e.forEach((a) => { a.color && i.set(a, t(a.color.toCss(!0))); }), i; } async _getRendererColors() { const { color: e } = await import("@amcharts/amcharts5/index.js"), t = /* @__PURE__ */ new Map(), i = this._getRenderer(), a = "default"; if (!i) return t; const n = await J(i); return n.delete(a), Array.from(n.values()).every((o) => o?.length === 1) && Array.from(n.keys()).forEach((o) => { const r = n.get(o)?.[0]?.toCss(!0); r && t.set(o, e(r)); }), t; } async _createChart(e) { const { activeMediaInfo: t } = this; if (!e || !t) return; const { createRoot: i } = await import("../../chunks/chart-utils.js"), a = await i(e); this._chartRoot = a, this._renderChart({ mediaInfo: t, root: a }); } _handleMediaKeyup(e) { const { key: t } = e; t === "ArrowLeft" && (e.stopPropagation(), this.previous()), t === "ArrowRight" && (e.stopPropagation(), this.next()); } _canAnimateChart() { return !this.chartAnimationDisabled && !this._prefersReducedMotion; } _getChartAnimationMS() { return this._canAnimateChart() ? ce : 0; } _getChartSeriesAnimationMS() { return this._canAnimateChart() ? de : 0; } async _customizeChartTooltip(e, t = "horizontal") { const { color: i } = await import("@amcharts/amcharts5/index.js"); e.setAll({ pointerOrientation: t }), e.get("background")?.setAll({ stroke: i(le) }), e.label.setAll({ direction: this._direction, oversizedBehavior: "wrap", maxWidth: ue }); } async _createPieChart(e, t) { const [{ Tooltip: i }, { PieChart: a, PieSeries: n }] = await Promise.all([ import("@amcharts/amcharts5/index.js"), import("@amcharts/amcharts5/percent.js") ]), { mediaInfo: s, root: o } = e, { title: r } = s, c = 5, m = s?.altText || s?.title || "", d = o.container.children.push(a.new(o, { role: "figure", ariaLabel: m, focusable: !0, paddingBottom: c, paddingTop: c, paddingLeft: c, paddingRight: c })), l = `{category}: {valuePercentTotal.formatNumber('0.00')}% ({value})`, p = i.new(o, { labelText: l }), u = d.series.push(n.new(o, { name: r, valueField: w, categoryField: C, tooltip: p })); u.ticks.template.set("forceHidden", !0), u.labels.template.set("forceHidden", !0), u.slices.template.states.create("active", { shiftRadius: c }), this._customizeChartTooltip(p), u.slices.template.setAll({ ariaLabel: l, focusable: !0, templateField: "columnSettings" }), u.data.setAll(t), u.appear(this._getChartSeriesAnimationMS()), d.appear(this._getChartAnimationMS()), d.root.dom.classList.toggle("chart-rendered", !0); } _getMinSeriesValue(e) { let t = 0; return e.forEach((i) => t = Math.min(i.value, t)), t; } async _createColumnChart(e, t, i) { const [{ Tooltip: a, Scrollbar: n }, { CategoryAxis: s, AxisRendererX: o, AxisRendererY: r, ValueAxis: c, ColumnSeries: m }] = await Promise.all([import("@amcharts/amcharts5/index.js"), import("@amcharts/amcharts5/xy.js")]), { mediaInfo: d, root: l } = t, { value: p, title: u } = d, { _direction: f } = this; e.setAll({ wheelX: "panX", wheelY: "zoomX" }); const h = e.xAxes.push(s.new(l, { renderer: o.new(l, { inversed: f === "rtl" }), categoryField: C })); h.get("renderer").labels.template.setAll({ forceHidden: !0 }); const g = e.yAxes.push(c.new(l, { renderer: r.new(l, { inside: !1 }), min: this._getMinSeriesValue(p.series) })); g.get("renderer").labels.template.setAll({ direction: f }); const x = "{categoryX}", I = a.new(l, { labelText: x }), v = e.series.push(m.new(l, { name: u, xAxis: h, yAxis: g, valueYField: w, categoryXField: C, tooltip: I })); this._customizeChartTooltip(I), v.columns.template.setAll({ ariaLabel: x, focusable: !0, templateField: "columnSettings" }), p.series.length > k && e.set("scrollbarX", n.new(l, { orientation: "horizontal" })), h.data.setAll(i), v.data.setAll(i), v.appear(this._getChartSeriesAnimationMS()), e.appear(this._getChartAnimationMS()); } async _createBarChart(e, t, i) { const [{ Tooltip: a, Scrollbar: n }, { CategoryAxis: s, AxisRendererX: o, AxisRendererY: r, ValueAxis: c, ColumnSeries: m }] = await Promise.all([import("@amcharts/amcharts5/index.js"), import("@amcharts/amcharts5/xy.js")]), { mediaInfo: d, root: l } = t, { value: p, title: u } = d, { _direction: f } = this; e.setAll({ wheelX: "panY", wheelY: "zoomY" }); const h = e.yAxes.push(s.new(l, { renderer: r.new(l, { inversed: !0 }), categoryField: C })); h.get("renderer").labels.template.setAll({ forceHidden: !0 }); const g = e.xAxes.push(c.new(l, { renderer: o.new(l, { inside: !1, inversed: f === "rtl" }), min: this._getMinSeriesValue(p.series) })); g.get("renderer").labels.template.setAll({ direction: f }); const x = "{categoryY}", I = a.new(l, { labelText: x }), v = e.series.push(m.new(l, { name: u, xAxis: g, yAxis: h, valueXField: w, categoryYField: C, tooltip: I })); this._customizeChartTooltip(I, "vertical"), v.columns.template.setAll({ ariaLabel: x, focusable: !0, templateField: "columnSettings" }), p.series.length > k && e.set("scrollbarY", n.new(l, { orientation: "vertical" })), h.data.setAll(i), v.data.setAll(i), v.appear(this._getChartSeriesAnimationMS()), e.appear(this._getChartAnimationMS()); } async _createLineChart(e, t, i) { const [{ Tooltip: a, Scrollbar: n }, { CategoryAxis: s, AxisRendererX: o, AxisRendererY: r, ValueAxis: c, LineSeries: m }] = await Promise.all([import("@amcharts/amcharts5/index.js"), import("@amcharts/amcharts5/xy.js")]), { root: d, mediaInfo: l } = t, { value: p, title: u } = l, { _direction: f } = this; e.setAll({ wheelX: "panX", wheelY: "zoomX" }); const h = e.xAxes.push(s.new(d, { renderer: o.new(d, { inversed: f === "rtl" }), categoryField: C })); h.get("renderer").labels.template.setAll({ forceHidden: !0 }); const g = e.yAxes.push(c.new(d, { renderer: r.new(d, { inside: !1 }), min: this._getMinSeriesValue(p.series) })); g.get("renderer").labels.template.setAll({ direction: f }); const x = "{categoryX}", I = i[0]?.lineSettings?.stroke, v = a.new(d, { getFillFromSprite: !I, labelText: x }); I && v.get("background")?.setAll({ fill: I }); const M = e.series.push(m.new(d, { name: u, xAxis: h, yAxis: g, valueYField: w, categoryXField: C, tooltip: v })); M.strokes.template.setAll({ templateField: "lineSettings" }), this._customizeChartTooltip(v, "vertical"), p.series.length > k && e.set("scrollbarX", n.new(d, { orientation: "horizontal" })), h.data.setAll(i), M.data.setAll(i), M.appear(this._getChartSeriesAnimationMS()), e.appear(this._getChartAnimationMS()); } async _createXYChart(e, t) { const { XYChart: i, XYCursor: a } = await import("@amcharts/amcharts5/xy.js"), { root: n, mediaInfo: s } = e, { type: o } = s, r = s?.altText || s?.title || "", c = n.container.children.push(i.new(n, { role: "figure", ariaLabel: r, focusable: !0, panX: !0, panY: !0 })); c.set("cursor", a.new(n, {})), o === "column-chart" && await this._createColumnChart(c, e, t), o === "bar-chart" && await this._createBarChart(c, e, t), o === "line-chart" && await this._createLineChart(c, e, t), c.root.dom.classList.toggle("chart-rendered", !0); } _clearMediaRefreshTimer() { const { _refreshTimer: e } = this; e && (clearTimeout(e), this._refreshTimer = void 0); } _updateMediaInfoTimestamp(e) { const t = Date.now(); this._refreshIntervalInfo = { timestamp: t, sourceURL: e && this._getImageSource(e, t) }; } _setupMediaRefreshTimer() { this._clearMediaRefreshTimer(); const { activeMediaInfo: e } = this; e?.type === "image" && e.refreshInterval > 0 && this._setRefreshTimeout(e); } _setRefreshTimeout(e) { const { refreshInterval: t, value: i } = e, a = t * 6e4; this._updateMediaInfoTimestamp(i.sourceURL); const n = window.setInterval(() => { this._updateMediaInfoTimestamp(i.sourceURL); }, a); this._refreshTimer = n; } _getImageSource(e, t) { const i = e.includes("?") ? "&" : "?", [a, n = ""] = e.split("#"); return `${a}${i}timestamp=${t}${n ? "#" : ""}${n}`; } _updateChartElement(e) { this._chartRoot?.dispose(), this._chartRoot = void 0, e && this._createChart(e); } render() { return _`<div class="root" @keyup=${this._handleMediaKeyup}><arcgis-feature-element-info .heading=${this.heading} .description=${this.description} .headingLevel=${this.headingLevel}></arcgis-feature-element-info>${this._renderMedia()}</div>`; } _renderMedia() { const { formattedMediaInfoCount: e, activeMediaInfoIndex: t } = this, i = this._renderMediaText(); return e ? _`<div class="container">${this._renderMediaInfo()}<div class="item-navigation">${i}${e > 1 ? _`<div class="pagination">${this._renderMediaPageButton("previous")}<span class="pagination-text">${Z(this.messages?.pageText ?? "", { index: t + 1, total: e })}</span>${this._renderMediaPageButton("next")}</div>` : null}</div></div>` : null; } _renderMediaText() { const { activeMediaInfo: e } = this; if (!e) return null; const t = e?.title ? _`<div class="item-title" .innerHTML=${N.sanitize(e.title) ?? ""}></div>` : null, i = e?.caption ? _`<div class="item-caption" .innerHTML=${N.sanitize(e.caption) ?? ""}></div>` : null; return t || i ? _`<div class="item-text">${t}${i}</div>` : null; } _renderImageMediaInfo(e) { if (!e.value) return null; const { _refreshIntervalInfo: t } = this, { value: i, refreshInterval: a, altText: n, title: s } = e, { sourceURL: o, linkURL: r } = i, m = se(r ?? void 0) ? "_blank" : "_self", d = m === "_blank" ? "noreferrer" : "", l = a ? t : null, p = l ? l.sourceURL : o, u = _`<img alt=${(n || s) ?? y} src=${p ?? void 0 ?? y}>`; return (r ? _`<a href=${r ?? y} rel=${d ?? y} target=${m ?? y} title=${s ?? y}>${u}</a>` : null) ?? u; } _renderChartMediaInfo(e) { const { activeMediaInfoIndex: t, formattedMediaInfoCount: i } = this; return G(`media-${e.type}-${t}-${i}`, _`<div class="chart" ${j(this._updateChartElement)}></div>`); } _renderMediaInfoType() { const { activeMediaInfo: e } = this; return e ? e.type === "image" ? this._renderImageMediaInfo(e) : e.type.includes("chart") ? this._renderChartMediaInfo(e) : null : null; } _renderMediaInfo() { const { activeMediaInfo: e } = this; return e ? _`<div class="item-container"><div class="item">${this._renderMediaInfoType()}</div></div>` : null; } _renderMediaPageButton(e) { if (this.formattedMediaInfoCount < 2) return null; const t = e === "previous", i = t ? this.messages?.previous ?? "" : this.messages?.next ?? "", a = t ? "chevron-left" : "chevron-right", n = t ? this.previous : this.next; return _`<button .ariaLabel=${i} class="pagination-button" @click=${n} tabindex=0 title=${i ?? y} type=button><calcite-icon flip-rtl class="pagination-icon" .icon=${a} scale=s></calcite-icon></button>`; } async _renderChart(e) { const { root: t, mediaInfo: i } = e, { value: a, type: n } = i, [{ default: s }, { default: o }, { default: r }, { esriChartColorSet: c }, { ColorSet: m, Theme: d }] = await Promise.all([ import("@amcharts/amcharts5/themes/Animated.js"), import("@amcharts/amcharts5/themes/Dark.js"), import("@amcharts/amcharts5/themes/Responsive.js"), import("../../chunks/chartCommon.js"), import("@amcharts/amcharts5/index.js") ]), l = d.new(t); l.rule("ColorSet").set("colors", c), l.rule("ColorSet").set("reuse", !0); const p = [r.new(t), l]; ie(this.el) === "dark" && p.push(o.new(t)), this._canAnimateChart() && p.push(s.new(t)), t.setThemes(p); const u = await this._getRendererColors(), f = await this._getSeriesColors(a.series), h = m.new(t, {}), g = f.get(a.series[0]), x = g ? { lineSettings: { stroke: g } } : void 0, I = a.series.map((v, M) => { const b = f.get(v) || u.get(v.fieldName) || h.getIndex(M); return { [C]: v.tooltip, [w]: v.value, columnSettings: { fill: b, stroke: b }, ...x }; }).filter((v) => n === "pie-chart" ? v.value != null && v.value > 0 : !0); n === "pie-chart" ? this._createPieChart(e, I) : this._createXYChart(e, I); } } B("arcgis-feature-media", he); export { he as ArcgisFeatureMedia };