Skip to content

Source CursorPosition

Source CursorPosition

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/cursor-position/cursor-position.declaration.ts

packages/common/src/lib/widgets/cursor-position/cursor-position.declaration.ts
import { widgetFactorySvelte, type WidgetProps } from '$lib/api/managers/widget';
import { type CursorPositionConfig, cursorPositionConfigSchema } from './cursor-position.config';
import type { WidgetDeclaration } from '$lib/api/managers/widget/widget-declaration';
export const declaration = {
factory: () => import('./CursorPosition.svelte').then((CursorPosition) => widgetFactorySvelte(CursorPosition)),
schema: () => cursorPositionConfigSchema,
} satisfies WidgetDeclaration;
export type CursorPositionProps = WidgetProps<CursorPositionConfig>;

packages/common/src/lib/widgets/cursor-position/cursor-position.config.ts

packages/common/src/lib/widgets/cursor-position/cursor-position.config.ts
import { defineWidgetConfig } from '$lib/api/managers/configuration';
import { inToolbarSchemaFrom } from '$lib/api/managers/configuration/models/widget/widget-in-toolbar.schema';
import { i18nDataSchema, i18nSchemaFrom } from '$lib/api/managers/i18n';
import { z } from 'zod';
const projectionOptionSchema = z.object({
wkid: z.number(),
xLabel: i18nDataSchema.optional(),
label: i18nDataSchema.optional(),
yLabel: i18nDataSchema.optional(),
unit: i18nDataSchema.optional(),
dms: z.boolean().optional(),
precision: z.number().optional(),
});
export type CursorPositionProjectionOption = z.infer<typeof projectionOptionSchema>;
export const cursorPositionConfigSchema = defineWidgetConfig({
inToolbar: inToolbarSchemaFrom(false),
active: true,
i18n: i18nSchemaFrom({
'dropdown-label': {
fr: 'Coordonnées du pointeur en',
nl: 'NL - Coordonnées du pointeur en',
},
}),
config: z
.object({
defaultWkid: z.number().optional(),
allowEdit: z.boolean().default(true),
projectionOptions: projectionOptionSchema.array().default([
{
wkid: 3812,
},
{
wkid: 31370,
},
{
wkid: 4326,
label: {
fr: 'WGS84 (DD)',
nl: 'NL - WGS84 (DD)',
},
},
{
wkid: 4326,
label: {
fr: 'WGS84 (DMS)',
nl: 'NL - WGS84 (DMS)',
},
dms: true,
},
]),
})
.prefault({}),
});
export type CursorPositionConfig = z.infer<typeof cursorPositionConfigSchema>;

packages/common/src/lib/widgets/cursor-position/CursorPosition.svelte

packages/common/src/lib/widgets/cursor-position/CursorPosition.svelte
<script lang="ts">
import { getI18n } from '$lib/api/managers/i18n';
import { getProjectionFromWkid, type Projection, Projections } from '$lib/api/managers/projection';
import { getMapManager } from '$lib/api/map';
import { type CardinalLabels, formatDms, parseDms } from '$lib/api/utils/format.utils';
import { ApiSelect } from '$lib/components/api-select';
import { onDestroy, tick } from 'svelte';
import { type CursorPositionProjectionOption } from './cursor-position.config';
import type { ApiPoint } from '$lib/api/geometry';
import { cn } from '$lib/components/shadcn/utils';
import type { CursorPositionProps } from './cursor-position.declaration';
type ResultFormat = {
x: string;
y: string;
xLabel: string;
yLabel: string;
unit: string;
};
const { fullConfig }: CursorPositionProps = $props();
const mapManager = getMapManager();
const zoom = mapManager.tools.zoom;
const transform = mapManager.tools.transform;
const i18n = getI18n(fullConfig.i18n);
const { config } = fullConfig;
const defaultWkid = config.defaultWkid;
const allowEdit = config.allowEdit;
const projections = $derived.by<CursorPositionProjectionOption[]>(() => {
return config.projectionOptions.map((option) => {
const projection = getProjectionFromWkid(option.wkid);
return mergeProjectionModelAndConfig(projection, option);
});
});
const defaultOption = defaultWkid ? projections.find((x) => x.wkid === defaultWkid) : projections[0];
let selectedProjection = $state<CursorPositionProjectionOption>(defaultOption);
const selectItems = projections.map((projection) => ({ value: projection, label: projection.label }));
$effect(() => {
if (selectedProjection) {
editMode = false;
}
});
const isWGS84 = $derived.by(() => {
return selectedProjection.wkid === Projections.WGS_1984.wkid;
});
const cardinalLabels: CardinalLabels = $derived.by(() => {
return {
north: i18n('common.cardinal-north'),
south: i18n('common.cardinal-south'),
west: i18n('common.cardinal-west'),
east: i18n('common.cardinal-east'),
};
});
let point = $state<ApiPoint | null>(null);
let editMode = $state<boolean>(false);
let xCoordValue = $state<number | string>(0);
let yCoordValue = $state<number | string>(0);
let xCoordInput = $state<HTMLInputElement | undefined>();
let yCoordInput = $state<HTMLInputElement | undefined>();
const formated = $derived.by(() => {
if (point) {
if (!selectedProjection) {
return {
x: point.x.toFixed(0),
y: point.y.toFixed(0),
xLabel: 'x',
yLabel: 'y',
unit: '',
} satisfies ResultFormat;
}
const projected = transform.transformPoint(point, selectedProjection.wkid);
if (selectedProjection.dms) {
return {
x: formatDms(projected.x, 'X', cardinalLabels),
y: formatDms(projected.y, 'Y', cardinalLabels),
xLabel: selectedProjection.xLabel,
yLabel: selectedProjection.yLabel,
unit: '',
} satisfies ResultFormat;
}
return {
x: projected.x.toFixed(selectedProjection.precision ?? 0),
y: projected.y.toFixed(selectedProjection.precision ?? 0),
xLabel: selectedProjection.xLabel ?? 'x',
yLabel: selectedProjection.yLabel ?? 'y',
unit: selectedProjection.unit ?? '',
} satisfies ResultFormat;
}
return null;
});
function mergeProjectionModelAndConfig(
projection: Projection,
option: CursorPositionProjectionOption,
): CursorPositionProjectionOption {
return {
wkid: projection.wkid,
dms: option.dms,
precision: option.precision ?? projection.precision,
xLabel: i18n.translate(option.xLabel ?? projection.xLabel),
yLabel: i18n.translate(option.yLabel ?? projection.yLabel),
unit: i18n.translate(option.unit ?? projection.unit),
label: i18n.translate(option.label ?? projection.label),
};
}
function validate(event: KeyboardEvent) {
if (event.key === 'Enter' && formated) {
let xCoord: string | number = xCoordValue;
let yCoord: string | number = yCoordValue;
if (selectedProjection.dms) {
const { x, y } = parseDms(`${xCoordValue}`, `${yCoordValue}`);
xCoord = x;
yCoord = y;
}
zoom.zoomToPoint({
x: Number(xCoord),
y: Number(yCoord),
wkid: selectedProjection.wkid,
});
editMode = false;
}
}
function activateEditMode(inputClick: HTMLInputElement | undefined) {
if (!allowEdit) {
return;
}
editMode = true;
if (!formated) return;
xCoordValue = formated.x;
yCoordValue = formated.y;
tick().then(() => inputClick?.focus());
}
const unsub = mapManager.tools.events.on('hover', (p) => (point = p));
onDestroy(() => {
unsub();
});
</script>
<div
class="gv-bg-black/40 gv-text-white gv-flex gv-items-center gv-h-5 gv-gap-1 gv-px-1 gv-py-0.5 gv-text-sm gv-leading-3"
>
<span>{i18n('dropdown-label')}</span>
<ApiSelect
bind:value={selectedProjection}
options={selectItems}
getKey={(p) => `${p.label}-${p.wkid}-${p.dms}`}
class="gv-h-5 gv-bg-transparent gv-w-[12rem] gv-border-none"
contentClass="gv-bg-black/40 gv-text-white"
dataTestId="CursorPosition-ProjectionSelect"
/>
{#if formated}
<span>
{formated.xLabel} =
<input
onkeydown={validate}
bind:this={xCoordInput}
bind:value={xCoordValue}
class={cn(editMode ? 'gv-visible' : 'gv-hidden', 'gv-bg-black/50 focus:gv-outline-none gv-m-0')}
type={isWGS84 ? 'text' : 'number'}
data-test-id="CursorPosition-XInput"
/>
<button
class={cn(editMode ? 'gv-hidden' : 'gv-visible', !allowEdit && 'gv-cursor-default')}
onclick={() => activateEditMode(xCoordInput)}
>
{formated.x}
</button>
<span class="gv-mr-3">{formated.unit}</span>
{formated.yLabel} =
<input
onkeydown={validate}
bind:this={yCoordInput}
bind:value={yCoordValue}
class={cn(editMode ? 'gv-visible' : 'gv-hidden', 'gv-bg-black/50 focus:gv-outline-none gv-m-0')}
type={isWGS84 ? 'text' : 'number'}
data-test-id="CursorPosition-YInput"
/>
<button
class={cn(editMode ? 'gv-hidden' : 'gv-visible', !allowEdit && 'gv-cursor-default')}
onclick={() => activateEditMode(yCoordInput)}
>
{formated.y}
</button>
{formated.unit}
</span>
{/if}
</div>
<style>
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type='number'] {
-moz-appearance: textfield;
}
</style>

packages/common/src/lib/widgets/cursor-position/index.ts

packages/common/src/lib/widgets/cursor-position/index.ts
export * from './cursor-position.config';
export * as CursorPosition from './CursorPosition.svelte';
export * from './cursor-position.declaration';

Aller plus loin