Skip to content

Source Export

Source Export

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

packages/common/src/lib/widgets/export/export/export.declaration.ts
import { widgetFactorySvelte, type WidgetProps } from '$lib/api/managers/widget';
import { type ExportConfig, exportConfigSchema } from './export.config';
import type { WidgetDeclaration } from '$lib/api/managers/widget/widget-declaration';
export const declaration = {
factory: () => import('./Export.svelte').then((Export) => widgetFactorySvelte(Export)),
schema: () => exportConfigSchema,
} satisfies WidgetDeclaration;
export type ExportProps = WidgetProps<ExportConfig>;

packages/common/src/lib/widgets/export/export/export.config.ts

packages/common/src/lib/widgets/export/export/export.config.ts
import { defineWidgetConfig, inToolbarSchemaFrom } from '$lib/api/managers/configuration';
import { type I18nRegistry, i18nSchemaFrom } from '$lib/api/managers/i18n';
import { z } from 'zod';
import { ExportFormat } from '$lib/api/tools';
export const exportTranslations = {
orientation: {
fr: 'Orientation du fichier',
nl: 'NL - Orientation du fichier',
},
vertical: {
fr: 'Portrait',
nl: 'NL - Portrait',
},
horizontal: {
fr: 'Paysage',
nl: 'NL - Paysage',
},
format: {
fr: 'Format',
nl: 'NL - Format',
},
title: {
fr: 'Titre',
nl: 'NL - Titre',
},
description: {
fr: 'Description',
nl: 'NL - Description',
},
name: {
fr: 'Nom',
nl: 'NL - Nom',
},
legend: {
fr: 'Légende',
nl: 'NL - Légende',
},
'show-legend': {
fr: 'Afficher la légende',
nl: 'NL - Afficher la légende',
},
'generate-the-format': {
fr: 'Générer le {{format}}',
nl: 'NL - Générer le {{format}}',
},
} satisfies I18nRegistry;
export const exportConfigSchema = defineWidgetConfig({
i18n: i18nSchemaFrom(exportTranslations),
title: {
fr: 'Exporter la carte',
nl: 'NL - Exporter la carte',
},
icon: {
lucide: 'Printer',
},
inToolbar: inToolbarSchemaFrom({
type: 'button',
}),
onActivate: {
deactivate: {
classes: ['Report', 'Identify', 'AddData', 'Draw', 'MeasureDistance', 'MeasureSurface', 'AdvancedSearch'],
},
},
config: z
.object({
allowedExtensions: z.array(z.enum(ExportFormat)).default([ExportFormat.PDF, ExportFormat.PNG]),
templateApiUrl: z.string().default('https://geoservices.test.wallonie.be/geoviewer-services/api/template'),
})
.optional()
.prefault({}),
});
export type ExportConfig = z.infer<typeof exportConfigSchema>;

packages/common/src/lib/widgets/export/export/Export.svelte

packages/common/src/lib/widgets/export/export/Export.svelte
<script lang="ts">
import { Switch } from '$lib/components/shadcn/ui/switch';
import { File } from 'lucide-svelte';
import { Item, RadioGroup } from '$lib/components/shadcn/ui/radio-group';
import { Label } from '$lib/components/shadcn/ui/label';
import { Input } from '$lib/components/shadcn/ui/input';
import { Textarea } from '$lib/components/shadcn/ui/textarea';
import { Checkbox } from '$lib/components/shadcn/ui/checkbox';
import { Button } from '$lib/components/shadcn/ui/button';
import { GeoviewerError } from '$lib/api/managers/error/geoviewer-error.model';
import { getMapManager } from '$lib/api/map';
import { downloadBlob, reverse } from '$lib/api/utils';
import { ExportFormat, ExportOrientation, TemplateParams } from '$lib/api/tools';
import { getI18n } from '$lib/api/managers/i18n';
import BlockingLoader from '$lib/components/blocking-loader/BlockingLoader.svelte';
import { LegendNode } from '$lib/api/utils/legend/legend.model.svelte';
import { getTitle, loadLegend } from '$lib/api/tools/report/report.utils';
import { cn } from '$lib/components/shadcn/utils';
import type { ExportProps } from './export.declaration';
let { fullConfig }: ExportProps = $props();
const mapManager = getMapManager();
const { config } = fullConfig;
const report = mapManager.tools.report;
const i18n = getI18n(fullConfig.i18n);
const legendNodes = $derived.by(() =>
reverse(mapManager.layerList.list)
.filter((x) => x.toc.visible && x.visible)
.map((service) => new LegendNode(service)),
);
let templateParams = new TemplateParams(config.templateApiUrl);
let vertical = $state(false);
let loading = $state(false);
$effect(() => {
templateParams.orientation = vertical ? ExportOrientation.VERTICAL : ExportOrientation.HORIZONTAL;
});
async function exportClicked() {
try {
loading = true;
await mapManager.waitForAllMapServicesToBeLoaded();
const pictureBlob = await report.downloadAdvancedScreenshot(templateParams.orientation);
templateParams.fileName = getTitle();
if (templateParams.format === ExportFormat.PNG) {
downloadBlob(pictureBlob, `${templateParams.fileName}.png`);
} else {
templateParams.base64Map = await blobToBase64(pictureBlob);
await generateReport();
}
} catch (error) {
console.error(error);
throw new GeoviewerError(i18n('common.export-error'), { cause: error });
} finally {
loading = false;
}
}
function blobToBase64(blob: Blob): Promise<string> {
return new Promise<string>((resolve) => {
const reader = new FileReader();
reader.onloadend = async () => {
resolve(reader.result as string);
};
reader.readAsDataURL(blob);
});
}
async function generateReport() {
if (templateParams.showLegend) {
templateParams.legendList = await loadLegend(legendNodes);
}
templateParams.templateName = `EXPORT_${templateParams.orientation.toString()}_${templateParams.format.toString()}`;
await report.downloadTemplate(templateParams);
}
</script>
<div class="gv-p-4">
<!--ORIENTATION-->
<div class="gv-font-extrabold gv-mb-2">{i18n('orientation')}</div>
<div class="gv-flex gv-align-middle gv-mb-2">
<button onclick={() => (vertical = false)} class={cn('gv-flex gv-align-middle', vertical && 'gv-opacity-60')}>
<File class="gv-ml-1 gv-h-6 gv-rotate-90" />
{i18n('horizontal')}
</button>
<Switch data-test-id="Export-orientation-switch" bind:checked={vertical} class="gv-mx-2" />
<button onclick={() => (vertical = true)} class={cn('gv-flex gv-align-middle', !vertical && 'gv-opacity-60')}>
{i18n('vertical')}
<File class="gv-h-5" />
</button>
</div>
<!--FORMAT-->
<div class="gv-font-extrabold gv-mb-2">{i18n('format')}</div>
<div class="gv-flex gv-mb-2">
<RadioGroup bind:value={templateParams.format} class="gv-flex gv-w-full gv-space-x-1">
{#each config.allowedExtensions as extension (extension)}
<div class="gv-flex gv-items-center gv-space-x-2">
<Item data-test-id="Export-extension-{extension}" value={extension} id={extension} />
<Label class="gv-cursor-pointer" for={extension}>{extension}</Label>
</div>
{/each}
</RadioGroup>
</div>
{#if templateParams.format !== ExportFormat.PNG}
<!--TITLE-->
<Label class="gv-block gv-font-extrabold gv-mb-2">{i18n('title')}</Label>
<Input
maxlength={100}
type="text"
data-test-id="Export-title-input"
bind:value={templateParams.title}
class="gv-mb-2"
/>
<!--DESCRIPTION-->
<Label class="gv-block gv-font-extrabold gv-mb-2">{i18n('description')}</Label>
<Textarea
maxlength={500}
data-test-id="Export-description-input"
bind:value={templateParams.description}
rows={3}
class="gv-mb-2"
/>
<!--LEGEND-->
<div class="gv-font-extrabold gv-mb-2">{i18n('legend')}</div>
<div class="gv-flex gv-items-center gv-space-x-2 gv-mb-2">
<Checkbox data-test-id="Export-legend-checkbox" bind:checked={templateParams.showLegend} id="legend" />
<Label for="legend">{i18n('show-legend')}</Label>
</div>
{/if}
<Button class="gv-mt-3" data-test-id="Export-export-button" onclick={exportClicked} size="sm">
{i18n('generate-the-format', { format: templateParams.format })}
</Button>
</div>
<BlockingLoader open={loading} />

Aller plus loin