Source UrlQuery
Source UrlQuery
Cette page est générée automatiquement à partir du dépôt local au moment de la génération de la documentation.
Fichiers inclus
packages/common/src/lib/widgets/url-query/url-query.declaration.tspackages/common/src/lib/widgets/url-query/url-query.config.tspackages/common/src/lib/widgets/url-query/url-query.models.tspackages/common/src/lib/widgets/url-query/UrlQuery.sveltepackages/common/src/lib/widgets/url-query/UrlQueryPopupContent.svelte
packages/common/src/lib/widgets/url-query/url-query.declaration.ts
import { widgetFactorySvelte, type WidgetProps } from '$lib/api/managers/widget';import { type UrlQueryFullConfig, urlQueryFullConfig } from '$lib/widgets/url-query/url-query.config';import type { WidgetDeclaration } from '$lib/api/managers/widget/widget-declaration';
export const declaration = { factory: () => import('./UrlQuery.svelte').then((UrlQuery) => widgetFactorySvelte(UrlQuery)), schema: () => urlQueryFullConfig,} satisfies WidgetDeclaration;
export type UrlQueryProps = WidgetProps<UrlQueryFullConfig>;packages/common/src/lib/widgets/url-query/url-query.config.ts
import { inToolbarSchemaFrom } from '$lib/api/managers/configuration/models/widget/widget-in-toolbar.schema';import { cadMapQueryParamsSchema, coordsQueryParamsSchema, defaultCadMapQueryParamsSchema, defaultCoordsQueryParamsSchema, defaultSpwSearchAllParams, esriLayerQueryParamsSchema, spwSearchAllParamsSchema,} from '$lib/api/tools/query';import { hiddenContainerId } from '$lib/components/containers/hidden/hidden.schema';import { z } from 'zod';import { defineWidgetConfig } from '$lib/api/managers/configuration/models/widget/widget-configuration.schema';import type { PopupPosition } from '$lib/api/managers/popup';
const urlAnchorBboxQuerySchema = z.object({ type: z.literal('BBOX'), wkid: z.number().optional().default(31370),});export type UrlAnchorBboxQuery = z.infer<typeof urlAnchorBboxQuerySchema>;const defaultUrlAnchorBboxQuery = urlAnchorBboxQuerySchema.parse({ type: 'BBOX' });
const urlAnchorCustomQuerySchema = z.object({ type: z.literal('CUSTOM'), anchor: z.string(), queryConfig: esriLayerQueryParamsSchema,});export type UrlAnchorCustomQuery = z.infer<typeof urlAnchorCustomQuerySchema>;
const urlAnchorCoordQuerySchema = z.object({ type: z.literal('COOR'), queryConfig: coordsQueryParamsSchema.optional().default(defaultCoordsQueryParamsSchema),});export type UrlAnchorCoorQuery = z.infer<typeof urlAnchorCoordQuerySchema>;const defaultUrlAnchorCoordQuery = urlAnchorCoordQuerySchema.parse({ type: 'COOR' });
const urlAnchorAdrQuerySchema = z.object({ type: z.literal('ADR'), queryConfig: spwSearchAllParamsSchema.optional().default(defaultSpwSearchAllParams),});export type UrlAnchorAdrQuery = z.infer<typeof urlAnchorAdrQuerySchema>;const defaultUrlAnchorAdrQuery = urlAnchorAdrQuerySchema.parse({ type: 'ADR' });
const urlAnchorCadQuerySchema = z.object({ type: z.literal('CAD'), queryConfig: cadMapQueryParamsSchema.optional().default(defaultCadMapQueryParamsSchema),});export type UrlAnchorCadQuery = z.infer<typeof urlAnchorCadQuerySchema>;const defaultUrlAnchorCadQuery = urlAnchorCadQuerySchema.parse({ type: 'CAD' });
const urlAnchorAddQuerySchema = z.object({ type: z.literal('ADD'),});export type UrlAnchorAddQuery = z.infer<typeof urlAnchorAddQuerySchema>;const defaultUrlAnchorAddQuery = urlAnchorAddQuerySchema.parse({ type: 'ADD' });
const urlAnchorQuerySchema = z.union([ urlAnchorCustomQuerySchema, urlAnchorBboxQuerySchema, urlAnchorAdrQuerySchema, urlAnchorCadQuerySchema, urlAnchorCoordQuerySchema, urlAnchorAddQuerySchema,]);export type UrlAnchorQuery = z.infer<typeof urlAnchorQuerySchema>;
const defaultAnchors = [ defaultUrlAnchorBboxQuery, defaultUrlAnchorCoordQuery, defaultUrlAnchorAdrQuery, defaultUrlAnchorCadQuery, defaultUrlAnchorAddQuery,];const urlQueryConfig = z.object({ anchors: z.array(urlAnchorQuerySchema).optional().default(defaultAnchors), resultHighlightParamsSplitter: z.string().optional().default('|'), resultHighlightParamsValueSplitter: z.string().optional().default('='), popupPosition: z.custom<PopupPosition>().default('bottom-right'),});export const urlQueryFullConfig = defineWidgetConfig({ container: hiddenContainerId, inToolbar: inToolbarSchemaFrom(false), active: true, config: urlQueryConfig.optional().prefault({}),});
export type UrlQueryFullConfig = z.infer<typeof urlQueryFullConfig>;packages/common/src/lib/widgets/url-query/url-query.models.ts
import { z } from 'zod';import { MapServiceTypes } from '$lib/api/managers/configuration';
const urlAnchorResultHighlightParamsSchema = z.object({ show: z.boolean().optional().default(false), tooltipTitle: z.string().optional().default('Informations'), tooltipText: z.string().optional().default(''), tooltipOpen: z.boolean().optional().default(false), scale: z.number().optional().default(500), allowDelete: z.boolean().optional().default(true),});export type UrlAnchorResultHighlightParams = z.infer<typeof urlAnchorResultHighlightParamsSchema>;
export function getUrlAnchorResultHighlightParams(fromUrl: Record<string, string>): UrlAnchorResultHighlightParams { return urlAnchorResultHighlightParamsSchema.parse({ show: getBooleanValue(fromUrl['SHOW']), tooltipTitle: fromUrl['TOOLTIPTITLE'], tooltipText: fromUrl['TOOLTIPTEXT'], tooltipOpen: getBooleanValue(fromUrl['TOOLTIPOPEN']), scale: fromUrl['SCALE'] != undefined ? Number(fromUrl['SCALE']) : 500, allowDelete: getBooleanValue(fromUrl['ALLOWDELETE']), });}
const urlAnchorAddServiceParamsSchema = z.object({ type: z.custom<MapServiceTypes>().default(MapServiceTypes.ARCGIS_DYNAMIC), url: z.string().optional(), metadataId: z.string().optional(), visible: z.boolean().default(true), opacity: z.number().default(1), label: z.string().optional(), description: z.string().optional(),});export type UrlAnchorAddServiceParams = z.infer<typeof urlAnchorAddServiceParamsSchema>;
export function getUrlAnchorAddServiceParams(fromUrls: Record<string, string>[]): UrlAnchorAddServiceParams[] { return fromUrls.map((fromUrl) => { return urlAnchorAddServiceParamsSchema.parse({ type: fromUrl['TYPE'], url: fromUrl['URL'], description: fromUrl['DESCRIPTION'], metadataId: fromUrl['METADATAID'], visible: getBooleanValue(fromUrl['VISIBLE']), opacity: fromUrl['OPACITY'] != undefined ? Number(fromUrl['OPACITY']) : 1, label: fromUrl['LABEL'], }); });}
function getBooleanValue(urlValue: string): boolean | undefined { if (!urlValue) { return undefined; } return urlValue.toLowerCase() === 'true';}packages/common/src/lib/widgets/url-query/UrlQuery.svelte
<script lang="ts"> import type { UrlAnchorAdrQuery, UrlAnchorBboxQuery, UrlAnchorCadQuery, UrlAnchorCoorQuery, UrlAnchorCustomQuery, UrlAnchorQuery, } from './url-query.config'; import { fromBbox } from '$lib/api/domain/api-extent.utils'; import { UrlUtils } from '$lib/api/utils'; import { getUrlAnchorAddServiceParams, getUrlAnchorResultHighlightParams, type UrlAnchorAddServiceParams, type UrlAnchorResultHighlightParams, } from './url-query.models'; import PopupComponent from '$lib/components/popup/PopupComponent.svelte'; import UrlQueryPopupContent from './UrlQueryPopupContent.svelte'; import { onDestroy } from 'svelte'; import type { ApiFeature } from '$lib/api/feature'; import { isApiFeature } from '$lib/widgets/global-search/models/global.search.models'; import { initGraphicMapServiceConfiguration, mapServiceConfigWithDefaults } from '$lib/api/managers/configuration'; import type { UrlQueryProps } from './url-query.declaration'; import { getMapManager } from '$lib/api/map';
let { fullConfig }: UrlQueryProps = $props();
const { config } = fullConfig; const { popupPosition } = config; const mapManager = getMapManager(); const metawal = mapManager.services.metawal; const zoomTool = mapManager.tools.zoom; const geometryEngine = mapManager.tools.geometryEngine;
type QueryAnchor = UrlAnchorAdrQuery | UrlAnchorCadQuery | UrlAnchorCoorQuery | UrlAnchorCustomQuery;
let selectedAnchor: UrlAnchorQuery | undefined = selectCurrentAnchor(); let onFeatureClickUnsubscriber: () => void;
let open = $state<boolean>(false); let allowDelete = $state<boolean>(false); let title = $state<string>(''); let content = $state<string>(''); let queryFeature = $state<ApiFeature | undefined>();
const serviceIdentifier = 'UrlQueryMapserviceId'; const graphicMapService = mapManager.addGraphicMapService( initGraphicMapServiceConfiguration({ id: serviceIdentifier, label: serviceIdentifier, toc: { visible: false, }, }), );
let popupLocation = $derived.by(() => { if (queryFeature) return geometryEngine.getCenter(queryFeature); });
if (selectedAnchor) { startQuery(); }
function startQuery() { if (!selectedAnchor) { return; } const resultHighlightParams: UrlAnchorResultHighlightParams = getUrlAnchorResultHighlightParams(getPipeValue()); if (selectedAnchor.type === 'BBOX') { resolveBboxQuery(selectedAnchor, getAnchor(selectedAnchor.type)); } else if (selectedAnchor.type === 'ADR' || selectedAnchor.type === 'CAD' || selectedAnchor.type === 'COOR') { resolveAnchorQuery(selectedAnchor, getAnchor(selectedAnchor.type), resultHighlightParams); } else if (selectedAnchor.type === 'CUSTOM') { const searchedValue = getAnchor(selectedAnchor.anchor); if (searchedValue) { resolveAnchorQuery(selectedAnchor, searchedValue, resultHighlightParams); } } else if (selectedAnchor.type === 'ADD') { const pipeValues = getPipeValuesForAnchor(selectedAnchor.type); resolveAddQuery(getUrlAnchorAddServiceParams(pipeValues)); } }
function resolveAddQuery(addServicesParams: UrlAnchorAddServiceParams[]) { // Handle services added from URL addServicesParams .filter((param) => !!param.url && !param.metadataId) .forEach((params) => { if (params.url) { const mapServiceConfig = mapServiceConfigWithDefaults({ url: params.url, type: params.type, opacity: params.opacity, visible: params.visible, id: crypto.randomUUID(), label: params.label ?? '', }); mapManager.addMapService(mapServiceConfig); } });
// Handle services added from METADATA ID addServicesParams .filter((param) => !!param.metadataId && !param.url) .forEach((param) => { if (param.metadataId) { metawal.addMapServiceFromMetadataId(param.metadataId, param); } });
// Log error for each service without URL & METADATA ID addServicesParams .filter((param) => !param.metadataId && !param.url) .forEach((param) => { console.error( 'Unable to add this #ADD anchor to the map, as either METADATAID or URL must be defined', param, ); }); }
function resolveBboxQuery(bboxQuery: UrlAnchorBboxQuery, bbox: string | undefined) { if (!bbox) return; try { const extent = fromBbox(bbox, bboxQuery.wkid); zoomTool.zoomToExtent(extent); } catch (err) { console.log('Unable to zoom to extent', err); } }
export function resolveAnchorQuery( anchorQuery: QueryAnchor, searchedValue: string | undefined, resultHighlightParams: UrlAnchorResultHighlightParams, ) { if (!searchedValue) return; resolveQuery(decodeURIComponent(searchedValue), anchorQuery).then((res) => onQueryResults(res, resultHighlightParams), ); }
function onQueryResults(results: ApiFeature[], resultHighlightParams: UrlAnchorResultHighlightParams): void { if (!results || !results[0]) { return; } const feature = results[0]; zoomTool.zoomToFeature(feature); if (resultHighlightParams.show) { graphicMapService.addFeature(feature); if (onFeatureClickUnsubscriber) { onFeatureClickUnsubscriber(); } onFeatureClickUnsubscriber = mapManager.tools.events.listenToAnyGraphicMapServiceClick((results) => { const features = results ? [...results.values()].flat() : []; const matchingFeature = features.some((x) => x.id === feature.id); if (matchingFeature) { openPopup(resultHighlightParams, feature); } }); } if (resultHighlightParams.tooltipOpen) { openPopup(resultHighlightParams, feature); } }
function openPopup(resultParams: UrlAnchorResultHighlightParams, feature: ApiFeature): void { queryFeature = feature; title = resultParams.tooltipTitle; content = resultParams.tooltipText; allowDelete = resultParams.allowDelete; open = false; setTimeout(() => { open = true; }, 50); }
function resolveQuery(searchText: string, queryParams: QueryAnchor): Promise<ApiFeature[]> { return mapManager.services .dynamicQuery({ searchText: decodeURIComponent(searchText), queryParams: queryParams.queryConfig, }) .then((res) => { return res.filter((x) => isApiFeature(x)) as ApiFeature[]; }); }
function selectCurrentAnchor(): UrlAnchorQuery | undefined { let currentAnchor; config.anchors.forEach((anchor) => { if (anchor.type === 'ADR' && UrlUtils.getHashValue('ADR')) { currentAnchor = anchor; } else if (anchor.type === 'CAD' && UrlUtils.getHashValue('CAD')) { currentAnchor = anchor; } else if (anchor.type === 'COOR' && UrlUtils.getHashValue('COOR')) { currentAnchor = anchor; } else if (anchor.type === 'BBOX' && UrlUtils.getHashValue('BBOX')) { currentAnchor = anchor; } else if (anchor.type === 'CUSTOM' && UrlUtils.getHashValue(anchor.anchor)) { currentAnchor = anchor; } else if (anchor.type === 'ADD' && getPipeValuesForAnchor('ADD').length > 0) { currentAnchor = anchor; } return; }); return currentAnchor; }
function getAnchor(anchor: string): string | undefined { let anchorValue = UrlUtils.getHashValue(anchor); if (!anchorValue) return; if (anchorValue.indexOf(config.resultHighlightParamsSplitter) > -1) { anchorValue = anchorValue.slice(0, anchorValue.indexOf(config.resultHighlightParamsSplitter)); } return anchorValue; }
function getPipeValue(): Record<string, string> { const regExp = new RegExp(`\\|([A-Za-z0-9]+=[^|]+)`, 'g'); const match = [...window.location.hash.matchAll(regExp)]; const records: Record<string, string> = {};
match .map((m) => m[1]) .forEach((paramValue) => { const splitted: string[] = paramValue.split(config.resultHighlightParamsValueSplitter); return (records[splitted[0]] = decodeURIComponent(splitted[1])); }); return records; }
function getPipeValuesForAnchor(anchor: string): Record<string, string>[] { const regExp = new RegExp(`#${anchor}\\|([^#]+)`, 'g'); const match = [...window.location.hash.matchAll(regExp)]; const results: Record<string, string>[] = [];
match.forEach((m) => { const parameters = m[1].split(config.resultHighlightParamsSplitter); const record: Record<string, string> = {}; parameters.forEach((param) => { const [key, value] = param.split(config.resultHighlightParamsValueSplitter); if (key && value) { record[key] = decodeURIComponent(value); } }); results.push(record); });
return results; }
onDestroy(() => { if (onFeatureClickUnsubscriber) { onFeatureClickUnsubscriber(); } });</script>
{#if queryFeature} <PopupComponent {popupPosition} {title} {open} location={popupLocation}> <UrlQueryPopupContent {allowDelete} {mapManager} {content} {graphicMapService} feature={queryFeature} /> </PopupComponent>{/if}packages/common/src/lib/widgets/url-query/UrlQueryPopupContent.svelte
<script lang="ts"> import { Button } from '$lib/components/shadcn/ui/button'; import type { MapManager } from '$lib/api/map'; import { getI18n } from '$lib/api/managers/i18n'; import type { ApiFeature } from '$lib/api/feature'; import type { ApiGraphicsMapService } from '$lib/api/mapservices';
interface Props { feature: ApiFeature; mapManager: MapManager; content: string; allowDelete: boolean; graphicMapService: ApiGraphicsMapService; }
let { feature, mapManager, content, allowDelete, graphicMapService }: Props = $props();
const i18n = getI18n();
function deleteFeature(feature: ApiFeature) { graphicMapService.removeFeature(feature); mapManager.closePopup(); }</script>
<div> <div>{content}</div> {#if allowDelete} <div class="gv-flex gv-justify-end"> <Button onclick={() => deleteFeature(feature)}>{i18n('common.delete')}</Button> </div> {/if}</div>