You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ya...@apache.org on 2026/06/15 01:28:22 UTC
(airavata-portals) branch main updated: feat(admin): graph-first Experiment Statistics page (shadcn-vue AreaChart) (#230)
This is an automated email from the ASF dual-hosted git repository.
yasithdev pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
The following commit(s) were added to refs/heads/main by this push:
new 4cf44321d feat(admin): graph-first Experiment Statistics page (shadcn-vue AreaChart) (#230)
4cf44321d is described below
commit 4cf44321d381c07ed54164cf5aba19b7a885201b
Author: Yasith Jayawardana <ya...@gatech.edu>
AuthorDate: Sun Jun 14 21:28:16 2026 -0400
feat(admin): graph-first Experiment Statistics page (shadcn-vue AreaChart) (#230)
* Revamp Experiment Statistics admin page as a graph-first interface
Replace the cluttered Experiment Statistics page with a single cohesive
"filter-and-find" interface led by a multi-line time-series graph of
gateway experiment activity.
- Add a dependency-free, hand-rolled SVG multi-line chart
(ExperimentStatisticsChart.vue): responsive viewBox, axes/gridlines,
one polyline per status series in the series color, a hover tooltip
showing the bucket time and per-series counts, and a legend that
doubles as on/off toggles. Series colors use the shadcn design tokens
(--primary, --chart-3, --warning, --success, --muted-foreground,
--destructive) for all/created/running/completed/cancelled/failed.
- Bucket the selected date range client-side and issue one aggregate
ExperimentStatistics request per bucket concurrently (Promise.all),
since there is no time-series endpoint. Granularity adapts to the range
(hourly / daily / weekly) and the bucket count is capped at 30. A
loading overlay covers the chart while requests are in flight.
- Provide a date-range picker plus Last 24 hours / Last week /
Last 30 days shortcuts, and inline optional scoping filters
(username / application / hostname) that scope both the graph and the
drill-down list.
- Remove the separate "Load experiment details" lookup card and the row
of six stat cards (delete the now-unused ExperimentStatisticsCard).
Keep the unique by-experiment-id / by-job-id lookup as a slim "Find an
experiment" affordance and a compact per-status experiments list with
the existing experiment-detail tabs/drill-down below the graph.
- Update the unit tests to match the new layout.
Keeps the /experiment-statistics route and name intact.
* Use shadcn-vue AreaChart for Experiment Statistics graph
Replace the hand-rolled SVG chart on the Experiment Statistics admin
page with the shadcn-vue AreaChart family (built on @unovis), and make
the chart compact and consistent with the design system.
- Vendor the shadcn-vue chart components under common's ui/ set
(ui/chart: ChartCrosshair, ChartLegend, ChartTooltip,
ChartSingleTooltip; ui/chart-area: AreaChart), converted from
TypeScript to the plain-JS <script setup> convention used by the rest
of the ui/ components. Cross-component imports use relative paths so
the components resolve under every consuming app's "@" alias
(admin, common, and workspace, which mirrors only a subset of ui/).
- Add @unovis/vue and @unovis/ts to the common package (the new
sanctioned charting dependency). CurveType is re-exported from the
chart-area barrel so consumers need no direct @unovis dependency.
- Wire Unovis' CSS variables to the design tokens in app.css and size
the axis/tooltip fonts to the design-system scale (text-xs), so the
chart reads consistently with the rest of the UI.
- Render the chart as a compact ~256px-tall (h-64), responsive-width
area chart with a smooth monotone curve and translucent gradient area
fills, down from the previous oversized chart.
- Drive categories/data/colors from the existing client-side bucketing
and keep the independent per-series on/off toggle row (in the series
colors) and the date-range picker + shortcuts. Series colors stay on
the chart tokens: all=primary, created=chart-3, running=warning,
completed=success, cancelled=muted-foreground, failed=destructive.
- Remove the now-unused hand-rolled ExperimentStatisticsChart.vue and
stub the AreaChart in the unit test (jsdom cannot render @unovis).
Keeps the /experiment-statistics route and name intact.
* fix(admin): make the statistics AreaChart fill its container width
Unovis' VisXYContainer falls back to a fixed ~300px width when no width CSS is
applied, collapsing the area chart into a narrow box on the right. Add
.unovis-xy-container { width/height: 100% } so the chart fills its h-64 wrapper.
(Also de-duplicated the tooltip CSS vars.)
---
.../statistics/ExperimentStatisticsCard.vue | 90 --
.../statistics/ExperimentStatisticsContainer.vue | 1150 ++++++++++-------
.../ExperimentStatisticsContainer.spec.js | 119 +-
.../django_airavata/static/common/css/app.css | 33 +
.../js/components/ui/chart-area/AreaChart.vue | 173 +++
.../common/js/components/ui/chart-area/index.js | 4 +
.../js/components/ui/chart/ChartCrosshair.vue | 45 +
.../common/js/components/ui/chart/ChartLegend.vue | 65 +
.../js/components/ui/chart/ChartSingleTooltip.vue | 73 ++
.../common/js/components/ui/chart/ChartTooltip.vue | 36 +
.../static/common/js/components/ui/chart/index.js | 24 +
.../static/common/js/components/ui/index.js | 2 +
.../django_airavata/static/common/package.json | 2 +
.../django_airavata/static/common/yarn.lock | 1337 +++++++++++++++++++-
14 files changed, 2531 insertions(+), 622 deletions(-)
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsCard.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsCard.vue
deleted file mode 100644
index 1b2c42abb..000000000
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsCard.vue
+++ /dev/null
@@ -1,90 +0,0 @@
-<template>
- <Card class="statistics-card overflow-hidden">
- <CardHeader>
- <div class="text-right">
- <div class="statistic-count whitespace-nowrap">
- <abbr :title="count">{{ displayedCount }}</abbr>
- </div>
- <div>{{ title }}</div>
- </div>
- </CardHeader>
- <CardContent>
- <a
- href="#"
- class="text-primary hover:no-underline"
- @click.prevent="$emit('click')"
- >
- <slot name="link-text">
- <div v-for="state in states" :key="state.value">
- {{ shortName(state) }}
- </div>
- </slot>
- </a>
- </CardContent>
- </Card>
-</template>
-
-<script>
-export default {
- name: "experiment-statistics-card",
- props: {
- bgVariant: {
- type: String,
- default: "light",
- },
- headerTextVariant: {
- type: String,
- default: "dark",
- },
- linkVariant: {
- type: String,
- default: "primary",
- },
- count: {
- type: Number,
- required: true,
- },
- title: {
- type: String,
- required: true,
- },
- states: {
- type: Array,
- default: () => [],
- },
- },
- computed: {
- displayedCount() {
- // Round large numbers and display m for 10^6 and k for 10^3
- if (this.count >= Math.pow(10, 6)) {
- return (this.count / Math.pow(10, 6)).toFixed(0) + "m";
- } else if (this.count >= Math.pow(10, 3)) {
- return (this.count / Math.pow(10, 3)).toFixed(0) + "k";
- } else {
- return this.count;
- }
- },
- },
- methods: {
- // Render the prefix-stripped short alias (e.g. "COMPLETED") instead of the
- // raw proto member name ("EXPERIMENT_STATE_COMPLETED"), matching the rest of
- // the portal (see ExperimentStatusBadge).
- shortName(state) {
- return state.constructor.shortAlias(state.name);
- },
- },
-};
-</script>
-
-<style scoped>
-.statistic-count {
- font-size: 2.8rem;
- overflow: hidden;
-}
-.statistics-card {
- height: calc(100% - 30px);
-}
-abbr {
- text-decoration: none;
-}
-</style>
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
index 14a9c8a95..c5751ce33 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
@@ -1,365 +1,478 @@
<template>
<main-layout
title="Experiment Statistics"
- subtitle="Browse experiment activity and details across the gateway."
+ subtitle="Gateway experiment activity over time."
>
- <div class="space-y-4">
- <Card>
- <CardHeader>
- <CardTitle>Load experiment details</CardTitle>
- </CardHeader>
- <CardContent>
- <Tabs default-value="by-experiment-id">
- <TabsList>
- <TabsTrigger value="by-experiment-id"
- >By Experiment ID</TabsTrigger
- >
- <TabsTrigger value="by-job-id">By Job ID</TabsTrigger>
- </TabsList>
- <TabsContent value="by-experiment-id" class="pt-4">
- <div class="flex items-stretch gap-2">
- <Input
- v-model.trim="experimentId"
- placeholder="Experiment ID"
- @keydown.enter="
- experimentId && showExperimentDetails(experimentId)
- "
+ <Tabs v-model="activeTab" ref="tabs" class="space-y-4">
+ <TabsList>
+ <TabsTrigger value="statistics">Statistics</TabsTrigger>
+ <TabsTrigger
+ v-for="experimentTab in experimentDetailTabs"
+ :key="experimentTab.experiment.experiment_id"
+ :value="experimentTab.experiment.experiment_id"
+ >
+ {{ experimentTab.tabTitle }}
+ <a
+ href="#"
+ @click.prevent="
+ removeExperimentDetailTab(experimentTab.experiment.experiment_id)
+ "
+ class="text-muted-foreground ml-1"
+ >
+ <X class="size-3.5" />
+ <span class="sr-only">Close experiment tab</span>
+ </a>
+ </TabsTrigger>
+ </TabsList>
+
+ <TabsContent value="statistics" class="space-y-4">
+ <!-- Primary deliverable: the time-series graph with an inline filter
+ bar (date range + shortcuts + optional scoping filters). -->
+ <Card>
+ <CardContent class="space-y-4 pt-6">
+ <div class="flex flex-wrap items-center justify-between gap-3">
+ <div class="flex flex-wrap items-center gap-2">
+ <span
+ class="border-input flex items-center rounded-md border px-3"
+ >
+ <CalendarDays class="size-4" aria-hidden="true" />
+ </span>
+ <flat-pickr
+ v-model="dateRange"
+ :config="dateConfig"
+ @on-change="dateRangeChanged"
+ placeholder="Select a date range"
+ class="border-input focus-visible:border-ring focus-visible:ring-ring/50 h-9 w-56 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs outline-none focus-visible:ring-3"
/>
<Button
- :disabled="!experimentId"
- @click="showExperimentDetails(experimentId)"
- variant="default"
- >Load</Button
+ v-for="shortcut in rangeShortcuts"
+ :key="shortcut.key"
+ :variant="
+ activeShortcut === shortcut.key ? 'default' : 'outline'
+ "
+ size="sm"
+ @click="applyShortcut(shortcut.key)"
+ >{{ shortcut.label }}</Button
>
</div>
- </TabsContent>
- <TabsContent value="by-job-id" class="pt-4">
- <div class="flex items-stretch gap-2">
+ <div class="flex flex-wrap items-center gap-2">
+ <DropdownMenu>
+ <DropdownMenuTrigger as-child>
+ <Button variant="outline" size="sm">
+ <FilterIcon class="size-4" />
+ Add filter
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent>
+ <DropdownMenuItem
+ v-if="!usernameFilterEnabled"
+ @click="usernameFilterEnabled = true"
+ >Username</DropdownMenuItem
+ >
+ <DropdownMenuItem
+ v-if="!applicationNameFilterEnabled"
+ @click="applicationNameFilterEnabled = true"
+ >Application Name</DropdownMenuItem
+ >
+ <DropdownMenuItem
+ v-if="!hostnameFilterEnabled"
+ @click="hostnameFilterEnabled = true"
+ >Hostname</DropdownMenuItem
+ >
+ <DropdownMenuItem
+ v-if="allFiltersEnabled"
+ disabled
+ class="text-muted-foreground"
+ >No more filters</DropdownMenuItem
+ >
+ </DropdownMenuContent>
+ </DropdownMenu>
+ </div>
+ </div>
+
+ <!-- Active scoping filters, shown inline only when enabled. -->
+ <div
+ v-if="anyFilterEnabled"
+ class="flex flex-wrap items-center gap-2"
+ >
+ <div
+ v-if="usernameFilterEnabled"
+ class="flex items-stretch gap-1"
+ >
<Input
- v-model.trim="jobId"
- placeholder="Job ID"
- @keydown.enter="jobId && showExperimentDetailsForJobId(jobId)"
+ v-model="usernameFilter"
+ placeholder="Username"
+ class="h-9 w-48"
+ @keydown.enter="loadStatistics"
/>
<Button
- :disabled="!jobId"
- @click="showExperimentDetailsForJobId(jobId)"
- variant="default"
- >Load</Button
+ variant="outline"
+ size="icon"
+ @click="removeUsernameFilter"
>
+ <X class="size-4" />
+ <span class="sr-only">Remove username filter</span>
+ </Button>
</div>
- </TabsContent>
- </Tabs>
- </CardContent>
- </Card>
- <Card>
- <CardContent>
- <Tabs v-model="activeTab" ref="tabs">
- <TabsList>
- <TabsTrigger value="statistics">{{
- selectedExperimentsTabTitle
- }}</TabsTrigger>
- <TabsTrigger
- v-for="experimentTab in experimentDetailTabs"
- :key="experimentTab.experiment.experiment_id"
- :value="experimentTab.experiment.experiment_id"
+ <div
+ v-if="applicationNameFilterEnabled"
+ class="flex items-stretch gap-1"
>
- {{ experimentTab.tabTitle }}
- <a
- href="#"
- @click.prevent="
- removeExperimentDetailTab(
- experimentTab.experiment.experiment_id,
- )
- "
- class="text-muted-foreground ml-1"
+ <select
+ v-model="applicationNameFilter"
+ @change="loadStatistics"
+ class="border-input focus-visible:border-ring focus-visible:ring-ring/50 h-9 w-56 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs outline-none focus-visible:ring-3"
>
- <X class="size-3.5" />
- <span class="sr-only">Close experiment tab</span>
- </a>
- </TabsTrigger>
- </TabsList>
- <TabsContent value="statistics" class="space-y-4 pt-4">
- <Card>
- <CardHeader>
- <CardTitle>Filter Options</CardTitle>
- </CardHeader>
- <CardContent>
- <div class="mb-2 flex w-full items-stretch gap-2">
- <span
- class="border-input flex items-center rounded-md border px-3"
- >
- <CalendarDays class="size-4" aria-hidden="true" />
- </span>
- <flat-pickr
- :value="dateRange"
- :config="dateConfig"
- @on-change="dateRangeChanged"
- class="border-input focus-visible:border-ring focus-visible:ring-ring/50 h-9 flex-1 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs outline-none focus-visible:ring-3"
- />
- <Button @click="getPast24Hours" variant="outline"
- >Past 24 Hours</Button
- >
- <Button @click="getPastWeek" variant="outline"
- >Past Week</Button
- >
- </div>
- <DropdownMenu>
- <DropdownMenuTrigger as-child>
- <Button variant="outline" class="mb-2"
- >Add Filters</Button
- >
- </DropdownMenuTrigger>
- <DropdownMenuContent>
- <DropdownMenuItem
- v-if="!usernameFilterEnabled"
- @click="usernameFilterEnabled = true"
- >Username</DropdownMenuItem
- >
- <DropdownMenuItem
- v-if="!applicationNameFilterEnabled"
- @click="applicationNameFilterEnabled = true"
- >Application Name</DropdownMenuItem
- >
- <DropdownMenuItem
- v-if="!hostnameFilterEnabled"
- @click="hostnameFilterEnabled = true"
- >Hostname</DropdownMenuItem
- >
- </DropdownMenuContent>
- </DropdownMenu>
- <div
- v-if="usernameFilterEnabled"
- class="mb-2 flex items-stretch gap-2"
+ <option :value="null" disabled>Application name</option>
+ <option
+ v-for="opt in applicationNameOptions"
+ :key="opt.value"
+ :value="opt.value"
>
+ {{ opt.text }}
+ </option>
+ </select>
+ <Button
+ variant="outline"
+ size="icon"
+ @click="removeApplicationNameFilter"
+ >
+ <X class="size-4" />
+ <span class="sr-only">Remove application name filter</span>
+ </Button>
+ </div>
+ <div
+ v-if="hostnameFilterEnabled"
+ class="flex items-stretch gap-1"
+ >
+ <select
+ v-model="hostnameFilter"
+ @change="loadStatistics"
+ class="border-input focus-visible:border-ring focus-visible:ring-ring/50 h-9 w-56 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs outline-none focus-visible:ring-3"
+ >
+ <option :value="null" disabled>Compute resource</option>
+ <option
+ v-for="opt in hostnameOptions"
+ :key="opt.value"
+ :value="opt.value"
+ >
+ {{ opt.text }}
+ </option>
+ </select>
+ <Button
+ variant="outline"
+ size="icon"
+ @click="removeHostnameFilter"
+ >
+ <X class="size-4" />
+ <span class="sr-only">Remove hostname filter</span>
+ </Button>
+ </div>
+ </div>
+
+ <!-- Compact, smooth-curved area chart (shadcn-vue AreaChart, built
+ on @unovis). A loading overlay covers it while the per-bucket
+ requests are in flight. The chart's own legend is disabled in
+ favor of the independent on/off toggle row below. -->
+ <div class="relative h-64 w-full">
+ <div
+ v-if="loading"
+ class="absolute inset-0 z-10 flex items-center justify-center bg-background/60"
+ >
+ <Loader2 class="size-6 animate-spin text-muted-foreground" />
+ </div>
+ <area-chart
+ v-if="visibleCategories.length > 0 && chartData.length > 0"
+ :key="chartKey"
+ :data="chartData"
+ :categories="visibleCategories"
+ index="label"
+ :colors="visibleColors"
+ :curve-type="curveType"
+ :y-formatter="formatCount"
+ :show-legend="false"
+ :show-grid-line="true"
+ class="h-full"
+ />
+ <div
+ v-else
+ class="flex h-full items-center justify-center text-sm text-muted-foreground"
+ >
+ {{
+ chartData.length === 0
+ ? "No data for the selected range."
+ : "No series selected."
+ }}
+ </div>
+ </div>
+ <!-- Legend doubling as independent on/off toggles, in the series
+ colors, sized to the design-system text scale. -->
+ <div class="flex flex-wrap gap-x-4 gap-y-2">
+ <button
+ v-for="s in series"
+ :key="s.key"
+ type="button"
+ class="flex items-center gap-2 text-sm transition-opacity"
+ :class="s.visible ? 'opacity-100' : 'opacity-40'"
+ :aria-pressed="s.visible"
+ @click="toggleSeries(s.key)"
+ >
+ <span
+ class="inline-block h-0.5 w-4 rounded-full"
+ :style="{ backgroundColor: s.color }"
+ />
+ <span>{{ s.label }}</span>
+ <span class="text-muted-foreground tabular-nums">{{
+ s.total
+ }}</span>
+ </button>
+ </div>
+ <p class="text-xs text-muted-foreground">
+ {{ rangeSummary }}
+ </p>
+ </CardContent>
+ </Card>
+
+ <!-- Secondary "find" affordances: a compact lookup and a drill-down
+ experiments list for the selected status. -->
+ <div class="grid gap-4 lg:grid-cols-3">
+ <Card class="lg:col-span-1">
+ <CardHeader>
+ <CardTitle class="text-base">Find an experiment</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <Tabs default-value="by-experiment-id">
+ <TabsList>
+ <TabsTrigger value="by-experiment-id"
+ >By Experiment ID</TabsTrigger
+ >
+ <TabsTrigger value="by-job-id">By Job ID</TabsTrigger>
+ </TabsList>
+ <TabsContent value="by-experiment-id" class="pt-4">
+ <div class="flex items-stretch gap-2">
<Input
- v-model="usernameFilter"
- placeholder="Username"
- @keydown.enter="loadStatistics"
+ v-model.trim="experimentId"
+ placeholder="Experiment ID"
+ @keydown.enter="
+ experimentId && showExperimentDetails(experimentId)
+ "
/>
- <Button variant="outline" @click="removeUsernameFilter">
- <X class="size-4" />
- <span class="sr-only">Remove username filter</span>
- </Button>
- </div>
- <div
- v-if="applicationNameFilterEnabled"
- class="mb-2 flex items-stretch gap-2"
- >
- <select
- v-model="applicationNameFilter"
- @change="loadStatistics"
- class="border-input focus-visible:border-ring focus-visible:ring-ring/50 h-9 flex-1 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs outline-none focus-visible:ring-3"
- >
- <option :value="null" disabled>
- Select an application to filter on
- </option>
- <option
- v-for="opt in applicationNameOptions"
- :key="opt.value"
- :value="opt.value"
- >
- {{ opt.text }}
- </option>
- </select>
<Button
- variant="outline"
- @click="removeApplicationNameFilter"
+ :disabled="!experimentId"
+ @click="showExperimentDetails(experimentId)"
+ variant="default"
+ >Load</Button
>
- <X class="size-4" />
- <span class="sr-only"
- >Remove application name filter</span
- >
- </Button>
</div>
- <div
- v-if="hostnameFilterEnabled"
- class="mb-2 flex items-stretch gap-2"
- >
- <select
- v-model="hostnameFilter"
- @change="loadStatistics"
- class="border-input focus-visible:border-ring focus-visible:ring-ring/50 h-9 flex-1 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs outline-none focus-visible:ring-3"
- >
- <option :value="null" disabled>
- Select compute resource to filter on
- </option>
- <option
- v-for="opt in hostnameOptions"
- :key="opt.value"
- :value="opt.value"
- >
- {{ opt.text }}
- </option>
- </select>
- <Button variant="outline" @click="removeHostnameFilter">
- <X class="size-4" />
- <span class="sr-only">Remove hostname filter</span>
- </Button>
- </div>
- </CardContent>
- <CardFooter>
- <div class="flex w-full justify-end">
- <Button @click="loadStatistics" class="ml-auto"
- >Get Statistics</Button
+ </TabsContent>
+ <TabsContent value="by-job-id" class="pt-4">
+ <div class="flex items-stretch gap-2">
+ <Input
+ v-model.trim="jobId"
+ placeholder="Job ID"
+ @keydown.enter="
+ jobId && showExperimentDetailsForJobId(jobId)
+ "
+ />
+ <Button
+ :disabled="!jobId"
+ @click="showExperimentDetailsForJobId(jobId)"
+ variant="default"
+ >Load</Button
>
</div>
- </CardFooter>
- </Card>
- <div>
- <h2 class="mb-4 text-lg font-semibold">
- Experiment Statistics from {{ fromTimeDisplay }} to
- {{ toTimeDisplay }}
- </h2>
- </div>
- <div class="grid grid-cols-2 gap-4 md:grid-cols-3 xl:grid-cols-6">
- <experiment-statistics-card
- :count="experimentStatistics.all_experiment_count || 0"
- title="Total Experiments"
- @click="selectExperiments('all_experiments')"
- >
- <template v-slot:link-text>
- <span>All</span>
- </template>
- </experiment-statistics-card>
- <experiment-statistics-card
- :count="experimentStatistics.created_experiment_count || 0"
- :states="createdStates"
- title="Created Experiments"
- @click="selectExperiments('created_experiments')"
- >
- </experiment-statistics-card>
- <experiment-statistics-card
- :count="experimentStatistics.running_experiment_count || 0"
- :states="runningStates"
- title="Running Experiments"
- @click="selectExperiments('running_experiments')"
- >
- </experiment-statistics-card>
- <experiment-statistics-card
- :count="experimentStatistics.completed_experiment_count || 0"
- :states="completedStates"
- title="Completed Experiments"
- @click="selectExperiments('completed_experiments')"
- >
- </experiment-statistics-card>
- <experiment-statistics-card
- :count="experimentStatistics.cancelled_experiment_count || 0"
- :states="canceledStates"
- title="Cancelled Experiments"
- @click="selectExperiments('cancelled_experiments')"
- >
- </experiment-statistics-card>
- <experiment-statistics-card
- :count="experimentStatistics.failed_experiment_count || 0"
- :states="failedStates"
- title="Failed Experiments"
- @click="selectExperiments('failed_experiments')"
+ </TabsContent>
+ </Tabs>
+ </CardContent>
+ </Card>
+
+ <Card class="lg:col-span-2">
+ <CardHeader class="flex-row items-center justify-between space-y-0">
+ <CardTitle class="text-base">{{ selectedStatusLabel }}</CardTitle>
+ <select
+ v-model="selectedExperimentSummariesKey"
+ class="border-input focus-visible:border-ring focus-visible:ring-ring/50 h-9 w-48 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs outline-none focus-visible:ring-3"
+ >
+ <option
+ v-for="opt in summaryOptions"
+ :key="opt.value"
+ :value="opt.value"
>
- </experiment-statistics-card>
- </div>
- <div v-if="items.length > 0">
- <Card>
- <CardContent>
- <Table>
- <TableHeader>
- <TableRow>
- <TableHead v-for="field in fields" :key="field.key">
- {{ field.label }}
- </TableHead>
- </TableRow>
- </TableHeader>
- <TableBody>
- <TableRow
- v-for="item in items"
- :key="item.experiment_id"
- >
- <TableCell>{{ item.name }}</TableCell>
- <TableCell>{{ item.user_name }}</TableCell>
- <TableCell>
- <application-name
- :application-interface-id="item.execution_id"
- />
- </TableCell>
- <TableCell>
- <compute-resource-name
- :compute-resource-id="item.resource_host_id"
- />
- </TableCell>
- <TableCell
- ><human-date :date="item.creation_time"
- /></TableCell>
- <TableCell>
- <experiment-status-badge
- :status-name="item.experiment_status.name"
- />
- </TableCell>
- <TableCell>
- <a
- href="#"
- class="inline-flex items-center gap-1 text-primary hover:underline"
- @click.prevent="
- showExperimentDetails(item.experiment_id)
- "
- >
- View Details
- <BarChart3 class="size-4" aria-hidden="true" />
- </a>
- </TableCell>
- </TableRow>
- </TableBody>
- </Table>
- </CardContent>
- </Card>
- <pager
- v-if="experimentStatistics.all_experiment_count > 0"
- :paginator="experimentStatisticsPaginator"
- @next="experimentStatisticsPaginator.next()"
- @previous="experimentStatisticsPaginator.previous()"
- ></pager>
- </div>
- </TabsContent>
- <TabsContent
- v-for="experimentTab in experimentDetailTabs"
- :key="experimentTab.experiment.experiment_id"
- :value="experimentTab.experiment.experiment_id"
- class="pt-4"
- >
- <experiment-details-view :experiment="experimentTab.experiment" />
- </TabsContent>
- </Tabs>
- </CardContent>
- </Card>
- </div>
+ {{ opt.text }} ({{ totalForKey(opt.value) }})
+ </option>
+ </select>
+ </CardHeader>
+ <CardContent>
+ <Table v-if="items.length > 0">
+ <TableHeader>
+ <TableRow>
+ <TableHead v-for="field in fields" :key="field.key">
+ {{ field.label }}
+ </TableHead>
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ <TableRow v-for="item in items" :key="item.experiment_id">
+ <TableCell>{{ item.name }}</TableCell>
+ <TableCell>{{ item.user_name }}</TableCell>
+ <TableCell>
+ <application-name
+ :application-interface-id="item.execution_id"
+ />
+ </TableCell>
+ <TableCell>
+ <compute-resource-name
+ :compute-resource-id="item.resource_host_id"
+ />
+ </TableCell>
+ <TableCell
+ ><human-date :date="item.creation_time"
+ /></TableCell>
+ <TableCell>
+ <experiment-status-badge
+ :status-name="item.experiment_status.name"
+ />
+ </TableCell>
+ <TableCell>
+ <a
+ href="#"
+ class="inline-flex items-center gap-1 text-primary hover:underline"
+ @click.prevent="
+ showExperimentDetails(item.experiment_id)
+ "
+ >
+ View Details
+ <BarChart3 class="size-4" aria-hidden="true" />
+ </a>
+ </TableCell>
+ </TableRow>
+ </TableBody>
+ </Table>
+ <p v-else class="py-6 text-center text-sm text-muted-foreground">
+ No experiments in this range for the selected status.
+ </p>
+ <pager
+ v-if="experimentStatistics.all_experiment_count > 0"
+ :paginator="experimentStatisticsPaginator"
+ @next="experimentStatisticsPaginator.next()"
+ @previous="experimentStatisticsPaginator.previous()"
+ ></pager>
+ </CardContent>
+ </Card>
+ </div>
+ </TabsContent>
+
+ <TabsContent
+ v-for="experimentTab in experimentDetailTabs"
+ :key="experimentTab.experiment.experiment_id"
+ :value="experimentTab.experiment.experiment_id"
+ class="pt-4"
+ >
+ <experiment-details-view :experiment="experimentTab.experiment" />
+ </TabsContent>
+ </Tabs>
</main-layout>
</template>
<script>
-import { BarChart3, CalendarDays, X } from "@lucide/vue";
+import {
+ BarChart3,
+ CalendarDays,
+ Filter as FilterIcon,
+ Loader2,
+ X,
+} from "@lucide/vue";
import { errors, models, services, utils } from "django-airavata-api";
import { components, notifications } from "django-airavata-common-ui";
-import ExperimentStatisticsCard from "./ExperimentStatisticsCard";
+import {
+ AreaChart,
+ CurveType,
+} from "django-airavata-common-ui/js/components/ui/chart-area";
import ExperimentDetailsView from "./ExperimentDetailsView";
import moment from "moment";
+const MS_PER_HOUR = 60 * 60 * 1000;
+const MS_PER_DAY = 24 * MS_PER_HOUR;
+// Cap on concurrent per-bucket requests so long ranges stay responsive.
+const MAX_BUCKETS = 30;
+
+// The six status series plotted on the chart. `countKey`/`summaryKey` map to the
+// ExperimentStatistics model's aggregate count + ExperimentSummary list fields.
+// Colors use the shadcn design tokens (see app.css): neutral/primary for "all",
+// then distinct accessible hues per status.
+const SERIES_DEFS = [
+ {
+ key: "all",
+ label: "All",
+ countKey: "all_experiment_count",
+ summaryKey: "all_experiments",
+ color: "var(--primary)",
+ },
+ {
+ key: "created",
+ label: "Created",
+ countKey: "created_experiment_count",
+ summaryKey: "created_experiments",
+ color: "var(--chart-3)",
+ },
+ {
+ key: "running",
+ label: "Running",
+ countKey: "running_experiment_count",
+ summaryKey: "running_experiments",
+ color: "var(--warning)",
+ },
+ {
+ key: "completed",
+ label: "Completed",
+ countKey: "completed_experiment_count",
+ summaryKey: "completed_experiments",
+ color: "var(--success)",
+ },
+ {
+ key: "cancelled",
+ label: "Cancelled",
+ countKey: "cancelled_experiment_count",
+ summaryKey: "cancelled_experiments",
+ color: "var(--muted-foreground)",
+ },
+ {
+ key: "failed",
+ label: "Failed",
+ countKey: "failed_experiment_count",
+ summaryKey: "failed_experiments",
+ color: "var(--destructive)",
+ },
+];
+
export default {
name: "experiment-statistics-container",
data() {
- //fp_incr sets the time of the date to midnight.
- //Calculating from today midnight to tomorrow midnight.
- const fromTime = new Date().fp_incr(0);
- const toTime = new Date().fp_incr(1);
+ // Default range: the last 24 hours, ending now.
+ const toTime = new Date();
+ const fromTime = new Date(toTime.getTime() - MS_PER_DAY);
return {
experimentStatisticsPaginator: null,
- selectedExperimentSummariesKey: null,
- fromTime: fromTime,
- toTime: toTime,
+ selectedExperimentSummariesKey: "all_experiments",
+ fromTime,
+ toTime,
dateRange: [fromTime, toTime],
dateConfig: {
mode: "range",
- wrap: true,
dateFormat: "Y-m-d",
- maxDate: new Date().fp_incr(1),
+ maxDate: new Date(),
},
+ activeShortcut: "24h",
+ suppressDateChange: false,
+ loading: false,
+ // Per-bucket aggregate counts: buckets[i] holds the ExperimentStatistics
+ // results for the i-th time window.
+ bucketStats: [],
+ buckets: [],
+ seriesVisible: SERIES_DEFS.reduce((acc, def) => {
+ acc[def.key] = true;
+ return acc;
+ }, {}),
usernameFilterEnabled: false,
usernameFilter: null,
applicationNameFilterEnabled: false,
@@ -384,9 +497,11 @@ export default {
components: {
BarChart3,
CalendarDays,
+ FilterIcon,
+ Loader2,
X,
ExperimentDetailsView,
- ExperimentStatisticsCard,
+ "area-chart": AreaChart,
"application-name": components.ApplicationName,
"compute-resource-name": components.ComputeResourceName,
"human-date": components.HumanDate,
@@ -395,78 +510,119 @@ export default {
pager: components.Pager,
},
computed: {
+ rangeShortcuts() {
+ return [
+ { key: "24h", label: "Last 24 hours" },
+ { key: "week", label: "Last week" },
+ { key: "30d", label: "Last 30 days" },
+ ];
+ },
+ anyFilterEnabled() {
+ return (
+ this.usernameFilterEnabled ||
+ this.applicationNameFilterEnabled ||
+ this.hostnameFilterEnabled
+ );
+ },
+ allFiltersEnabled() {
+ return (
+ this.usernameFilterEnabled &&
+ this.applicationNameFilterEnabled &&
+ this.hostnameFilterEnabled
+ );
+ },
+ // The aggregate ExperimentStatistics for the whole range (used by the
+ // drill-down list + counts). Returned by the final/full-range request.
experimentStatistics() {
return this.experimentStatisticsPaginator
? this.experimentStatisticsPaginator.results
: {};
},
- createdStates() {
- // TODO: moved to ExperimentStatistics model
- return [models.ExperimentState.CREATED, models.ExperimentState.VALIDATED];
- },
- runningStates() {
- return [
- models.ExperimentState.SCHEDULED,
- models.ExperimentState.LAUNCHED,
- models.ExperimentState.EXECUTING,
- ];
- },
- completedStates() {
- return [models.ExperimentState.COMPLETED];
+ // Chart series: each status' per-bucket count, in the series color, with the
+ // current on/off visibility and a range total.
+ series() {
+ return SERIES_DEFS.map((def) => {
+ const values = this.bucketStats.map(
+ (stats) => (stats && stats[def.countKey]) || 0,
+ );
+ const total = values.reduce((sum, v) => sum + v, 0);
+ return {
+ key: def.key,
+ label: def.label,
+ color: def.color,
+ visible: this.seriesVisible[def.key],
+ values,
+ total,
+ };
+ });
},
- canceledStates() {
- return [
- models.ExperimentState.CANCELING,
- models.ExperimentState.CANCELED,
- ];
+ // Smooth (monotone) curve interpolation for the area/line series.
+ curveType() {
+ return CurveType.MonotoneX;
+ },
+ // The currently-visible series, used to build the chart categories/colors.
+ visibleSeries() {
+ return this.series.filter((s) => s.visible);
+ },
+ // Category keys (the series labels) for the AreaChart, in display order.
+ visibleCategories() {
+ return this.visibleSeries.map((s) => s.label);
+ },
+ // Remount the chart when the set of visible series changes so its internal
+ // legend/crosshair state stays in sync with the categories/colors. Data-only
+ // refreshes within the same set keep the same key (and animate in place).
+ chartKey() {
+ return this.visibleCategories.join("|");
+ },
+ // Colors aligned 1:1 with visibleCategories.
+ visibleColors() {
+ return this.visibleSeries.map((s) => s.color);
+ },
+ // One row per time bucket: { label: <x-axis tick>, <series label>: count }.
+ // Only visible series are included so the chart redraws on toggle.
+ chartData() {
+ return this.buckets.map((bucket, i) => {
+ const row = { label: bucket.label };
+ for (const s of this.visibleSeries) {
+ row[s.label] = s.values[i] ?? 0;
+ }
+ return row;
+ });
},
- failedStates() {
- return [models.ExperimentState.FAILED];
+ rangeSummary() {
+ const from = moment(this.fromTime).format("MMM D, YYYY HH:mm");
+ const to = moment(this.toTime).format("MMM D, YYYY HH:mm");
+ const n = this.buckets.length;
+ const granularity = n > 0 ? this.buckets[0].granularity : "";
+ return `${from} – ${to} · ${n} ${granularity} bucket${
+ n === 1 ? "" : "s"
+ }`;
},
fields() {
return [
- {
- key: "name",
- label: "Name",
- },
- {
- key: "user_name",
- label: "Owner",
- },
- {
- key: "execution_id",
- label: "Application",
- },
- {
- key: "resource_host_id",
- label: "Resource",
- },
- {
- key: "creation_time",
- label: "Creation Time",
- },
- {
- key: "experiment_status",
- label: "Status",
- },
- {
- key: "actions",
- label: "Actions",
- },
+ { key: "name", label: "Name" },
+ { key: "user_name", label: "Owner" },
+ { key: "execution_id", label: "Application" },
+ { key: "resource_host_id", label: "Resource" },
+ { key: "creation_time", label: "Creation Time" },
+ { key: "experiment_status", label: "Status" },
+ { key: "actions", label: "Actions" },
];
},
- items() {
- if (this.selectedExperimentSummaries) {
- return this.selectedExperimentSummaries;
- } else {
- return [];
- }
+ summaryOptions() {
+ return SERIES_DEFS.map((def) => ({
+ value: def.summaryKey,
+ text: `${def.label} Experiments`,
+ }));
},
- fromTimeDisplay() {
- return moment(this.fromTime).format("MMM Do YYYY");
+ selectedStatusLabel() {
+ const opt = this.summaryOptions.find(
+ (o) => o.value === this.selectedExperimentSummariesKey,
+ );
+ return opt ? opt.text : "Experiments";
},
- toTimeDisplay() {
- return moment(this.toTime).format("MMM Do YYYY");
+ items() {
+ return this.selectedExperimentSummaries;
},
selectedExperimentSummaries() {
if (
@@ -481,12 +637,10 @@ export default {
},
applicationNameOptions() {
if (this.appInterfaces) {
- const options = this.appInterfaces.map((appInterface) => {
- return {
- value: appInterface.application_interface_id,
- text: appInterface.application_name,
- };
- });
+ const options = this.appInterfaces.map((appInterface) => ({
+ value: appInterface.application_interface_id,
+ text: appInterface.application_name,
+ }));
return utils.StringUtils.sortIgnoreCase(options, (o) => o.text);
} else {
return [];
@@ -495,7 +649,6 @@ export default {
hostnameOptions() {
if (this.computeResourceNames && this.groupResourceProfiles) {
// Only show compute resources that are configured in the Group Resource Profiles
- // First create a Set of all compute resource ids in the GRPs
const groupResourceProfileCompResources = new Set(
this.groupResourceProfiles.flatMap((grp) =>
grp.compute_preferences.map((cp) => cp.compute_resource_id),
@@ -503,50 +656,164 @@ export default {
);
const options = this.computeResourceNames
.filter((name) => groupResourceProfileCompResources.has(name.host_id))
- .map((name) => {
- return {
- value: name.host_id,
- text: name.host,
- };
- });
+ .map((name) => ({
+ value: name.host_id,
+ text: name.host,
+ }));
return utils.StringUtils.sortIgnoreCase(options, (o) => o.text);
} else {
return [];
}
},
- selectedExperimentsTabTitle() {
- if (this.selectedExperimentSummariesKey === "all_experiments") {
- return "All Experiments";
- } else if (
- this.selectedExperimentSummariesKey === "created_experiments"
- ) {
- return "Created Experiments";
- } else if (
- this.selectedExperimentSummariesKey === "running_experiments"
- ) {
- return "Running Experiments";
- } else if (
- this.selectedExperimentSummariesKey === "completed_experiments"
- ) {
- return "Completed Experiments";
- } else if (
- this.selectedExperimentSummariesKey === "cancelled_experiments"
- ) {
- return "Cancelled Experiments";
- } else if (this.selectedExperimentSummariesKey === "failed_experiments") {
- return "Failed Experiments";
+ },
+ methods: {
+ // Compact count formatting for the Y axis (e.g. 1.2k, 3m).
+ formatCount(value) {
+ const n = Math.round(value);
+ if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, "") + "m";
+ if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, "") + "k";
+ return String(n);
+ },
+ totalForKey(summaryKey) {
+ const def = SERIES_DEFS.find((d) => d.summaryKey === summaryKey);
+ const stats = this.experimentStatistics;
+ return (def && stats && stats[def.countKey]) || 0;
+ },
+ toggleSeries(key) {
+ this.seriesVisible = {
+ ...this.seriesVisible,
+ [key]: !this.seriesVisible[key],
+ };
+ },
+ // Split [fromTime, toTime] into N time windows. Granularity adapts to the
+ // range length to keep the bucket count at or below MAX_BUCKETS: hourly for
+ // short ranges, daily for medium, weekly for long.
+ computeBuckets() {
+ const from = this.fromTime.getTime();
+ const to = this.toTime.getTime();
+ const span = Math.max(to - from, MS_PER_HOUR);
+
+ let step;
+ let granularity;
+ if (span <= MAX_BUCKETS * MS_PER_HOUR) {
+ step = MS_PER_HOUR;
+ granularity = "hourly";
+ } else if (span <= MAX_BUCKETS * MS_PER_DAY) {
+ step = MS_PER_DAY;
+ granularity = "daily";
} else {
- return "Experiments";
+ step = 7 * MS_PER_DAY;
+ granularity = "weekly";
+ }
+ // If even the chosen granularity exceeds the cap (very long custom ranges),
+ // widen the step so the count stays within MAX_BUCKETS.
+ let count = Math.ceil(span / step);
+ if (count > MAX_BUCKETS) {
+ step = Math.ceil(span / MAX_BUCKETS);
+ count = MAX_BUCKETS;
+ granularity = "interval";
+ }
+
+ const buckets = [];
+ for (let i = 0; i < count; i++) {
+ const start = from + i * step;
+ const end = Math.min(start + step, to);
+ if (start >= to) break;
+ buckets.push({
+ start,
+ end,
+ granularity,
+ label:
+ granularity === "hourly"
+ ? moment(start).format("HH:mm")
+ : moment(start).format("MMM D"),
+ });
+ }
+ return buckets;
+ },
+ baseRequestParams() {
+ const params = {};
+ if (this.usernameFilterEnabled && this.usernameFilter) {
+ params.userName = this.usernameFilter;
+ }
+ if (this.applicationNameFilterEnabled && this.applicationNameFilter) {
+ params.applicationName = this.applicationNameFilter;
+ }
+ if (this.hostnameFilterEnabled && this.hostnameFilter) {
+ params.resourceHostName = this.hostnameFilter;
+ }
+ return params;
+ },
+ async loadStatistics() {
+ this.loading = true;
+ const buckets = this.computeBuckets();
+ const baseParams = this.baseRequestParams();
+ try {
+ // One aggregate request per bucket, issued concurrently, plus one
+ // full-range request that feeds the drill-down list + total counts.
+ const bucketPromises = buckets.map((bucket) =>
+ services.ExperimentStatisticsService.get({
+ ...baseParams,
+ fromTime: new Date(bucket.start).toJSON(),
+ toTime: new Date(bucket.end).toJSON(),
+ }),
+ );
+ const fullRangePromise = services.ExperimentStatisticsService.get({
+ ...baseParams,
+ fromTime: this.fromTime.toJSON(),
+ toTime: this.toTime.toJSON(),
+ });
+ const [fullRange, ...bucketResults] = await Promise.all([
+ fullRangePromise,
+ ...bucketPromises,
+ ]);
+ this.experimentStatisticsPaginator = fullRange;
+ this.bucketStats = bucketResults.map((paginator) =>
+ paginator ? paginator.results : null,
+ );
+ this.buckets = buckets;
+ } catch (error) {
+ utils.FetchUtils.reportError(error);
+ } finally {
+ this.loading = false;
}
},
- },
- methods: {
dateRangeChanged(selectedDates) {
- [this.fromTime, this.toTime] = selectedDates;
- if (this.fromTime && this.toTime) {
+ // Ignore the onChange that flatpickr fires when a shortcut updates the
+ // picker programmatically; that path already set the range + reloaded.
+ if (this.suppressDateChange) {
+ return;
+ }
+ if (selectedDates.length === 2) {
+ this.fromTime = selectedDates[0];
+ // flatpickr range end is midnight of the end day; include the whole day.
+ this.toTime = new Date(selectedDates[1].getTime() + MS_PER_DAY);
+ this.activeShortcut = null;
this.loadStatistics();
}
},
+ applyShortcut(key) {
+ const to = new Date();
+ let from;
+ if (key === "24h") {
+ from = new Date(to.getTime() - MS_PER_DAY);
+ } else if (key === "week") {
+ from = new Date(to.getTime() - 7 * MS_PER_DAY);
+ } else {
+ from = new Date(to.getTime() - 30 * MS_PER_DAY);
+ }
+ this.fromTime = from;
+ this.toTime = to;
+ this.activeShortcut = key;
+ // Update the picker without letting its onChange clobber the shortcut
+ // state; reset the flag after the change has been flushed.
+ this.suppressDateChange = true;
+ this.dateRange = [from, to];
+ this.$nextTick(() => {
+ this.suppressDateChange = false;
+ });
+ this.loadStatistics();
+ },
loadApplicationInterfaces() {
return services.ApplicationInterfaceService.list().then(
(appInterfaces) => (this.appInterfaces = appInterfaces),
@@ -561,46 +828,6 @@ export default {
this.groupResourceProfiles =
await services.GroupResourceProfileService.list();
},
- loadStatistics() {
- const requestData = {
- fromTime: this.fromTime.toJSON(),
- toTime: this.toTime.toJSON(),
- };
- if (this.usernameFilterEnabled && this.usernameFilter) {
- requestData["userName"] = this.usernameFilter;
- }
- if (this.applicationNameFilterEnabled && this.applicationNameFilter) {
- requestData["applicationName"] = this.applicationNameFilter;
- }
- if (this.hostnameFilterEnabled && this.hostnameFilter) {
- requestData["resourceHostName"] = this.hostnameFilter;
- }
- return services.ExperimentStatisticsService.get(requestData).then(
- (stats) => {
- this.experimentStatisticsPaginator = stats;
- },
- );
- },
- getPast24Hours() {
- this.fromTime = new Date().fp_incr(0);
- //this.fromTime = new Date(this.fromTime.setHours(0,0,0));
- this.toTime = new Date().fp_incr(1);
- this.updateDateRange();
- },
- getPastWeek() {
- this.fromTime = new Date().fp_incr(-7);
- this.toTime = new Date().fp_incr(1);
- this.updateDateRange();
- },
- updateDateRange() {
- this.dateRange = [
- moment(this.fromTime).format("YYYY-MM-DD"),
- moment(this.toTime).format("YYYY-MM-DD"),
- ];
- },
- daysAgo(days) {
- return new Date(Date.now() - days * 24 * 60 * 60 * 1000);
- },
removeUsernameFilter() {
this.usernameFilter = null;
this.usernameFilterEnabled = false;
@@ -705,15 +932,6 @@ export default {
scrollTabsIntoView() {
this.$refs.tabs.$el.scrollIntoView({ behavior: "smooth" });
},
- selectExperiments(experimentSummariesKey) {
- if (
- this.experimentStatisticsPaginator &&
- this.experimentStatisticsPaginator.offset > 0
- ) {
- this.loadStatistics();
- }
- this.selectedExperimentSummariesKey = experimentSummariesKey;
- },
},
};
</script>
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/tests/unit/components/statistics/ExperimentStatisticsContainer.spec.js b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/tests/unit/components/statistics/ExperimentStatisticsContainer.spec.js
index 1ed0ef276..f88500f49 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/tests/unit/components/statistics/ExperimentStatisticsContainer.spec.js
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/tests/unit/components/statistics/ExperimentStatisticsContainer.spec.js
@@ -35,6 +35,19 @@ function renderWithProviders(component) {
return render(Wrapper, renderOptions);
}
+// The AreaChart is built on @unovis, which renders via SVG/measurement APIs that
+// jsdom does not implement. These tests exercise the lookup + filter logic, not
+// the chart rendering, so stub the chart-area module with a trivial component.
+vi.mock("django-airavata-common-ui/js/components/ui/chart-area", () => ({
+ __esModule: true,
+ AreaChart: {
+ name: "area-chart-stub",
+ props: ["data", "categories", "index", "colors", "curveType", "yFormatter"],
+ template: '<div data-testid="area-chart-stub" />',
+ },
+ CurveType: { MonotoneX: "monotoneX" },
+}));
+
import { models, services, utils } from "django-airavata-api";
import ExperimentStatus from "django-airavata-api/static/django_airavata_api/js/models/ExperimentStatus";
vi.mock("django-airavata-api", async () => {
@@ -72,6 +85,36 @@ vi.mock("django-airavata-api", async () => {
};
});
+// The container issues one ExperimentStatistics request per time bucket plus a
+// full-range request, all concurrently. Return a fresh empty-stats paginator on
+// every call so a single mock satisfies them all.
+function emptyStatsPaginator() {
+ return new utils.PaginationIterator(
+ {
+ count: 0,
+ next: null,
+ previous: null,
+ results: {
+ all_experiment_count: 0,
+ completed_experiment_count: 0,
+ cancelled_experiment_count: 0,
+ failed_experiment_count: 0,
+ created_experiment_count: 0,
+ running_experiment_count: 0,
+ all_experiments: [],
+ completed_experiments: [],
+ failed_experiments: [],
+ cancelled_experiments: [],
+ created_experiments: [],
+ running_experiments: [],
+ },
+ limit: 50,
+ offset: 0,
+ },
+ models.ExperimentStatistics,
+ );
+}
+
beforeEach(() => {
vi.resetAllMocks();
@@ -86,31 +129,8 @@ beforeEach(() => {
test("load experiment by job id when job id matches unique experiment", async () => {
// Service call mocks
services.ApplicationInterfaceService.list.mockResolvedValue([]);
- services.ExperimentStatisticsService.get.mockResolvedValue(
- new utils.PaginationIterator(
- {
- count: 0,
- next: null,
- previous: null,
- results: {
- all_experiment_count: 0,
- completed_experiment_count: 0,
- cancelled_experiment_count: 0,
- failed_experiment_count: 0,
- created_experiment_count: 0,
- running_experiment_count: 0,
- all_experiments: [],
- completed_experiments: [],
- failed_experiments: [],
- cancelled_experiments: [],
- created_experiments: [],
- running_experiments: [],
- },
- limit: 50,
- offset: 0,
- },
- models.ExperimentStatistics
- )
+ services.ExperimentStatisticsService.get.mockImplementation(() =>
+ Promise.resolve(emptyStatsPaginator()),
);
services.ComputeResourceService.namesList.mockResolvedValue([]);
services.ExperimentSearchService.list.mockResolvedValue(
@@ -121,8 +141,8 @@ test("load experiment by job id when job id matches unique experiment", async ()
previous: null,
results: [{ experiment_id: "test-experiment-id" }],
},
- models.ExperimentSummary
- )
+ models.ExperimentSummary,
+ ),
);
// Mock just enough of Experiment and FullExperiment to get ExperimentDetailsView to render
const experiment = new models.Experiment({
@@ -141,7 +161,7 @@ test("load experiment by job id when job id matches unique experiment", async ()
new models.FullExperiment({
experiment_id: "test-experiment-id",
experiment,
- })
+ }),
);
services.ExperimentArchiveService.get.mockResolvedValue({
archived: false,
@@ -152,7 +172,7 @@ test("load experiment by job id when job id matches unique experiment", async ()
// The render method returns a collection of utilities to query your component.
const { findByText, findByPlaceholderText } = renderWithProviders(
- ExperimentStatisticsContainer
+ ExperimentStatisticsContainer,
);
const byJobIDTab = await findByText("By Job ID");
@@ -169,7 +189,7 @@ test("load experiment by job id when job id matches unique experiment", async ()
await fireEvent.update(jobIDInputField, "12345");
const loadButton = await within(jobIDInputField.parentElement).findByText(
- "Load"
+ "Load",
);
await fireEvent.click(loadButton);
@@ -186,7 +206,7 @@ test("load experiment by job id when job id matches unique experiment", async ()
},
{
ignoreErrors: true,
- }
+ },
);
// The experiment-details tab is activated asynchronously (setTimeout) and its
// panel (ExperimentDetailsView) mounts lazily on activation, which is when it
@@ -194,38 +214,15 @@ test("load experiment by job id when job id matches unique experiment", async ()
await waitFor(() =>
expect(services.FullExperimentService.retrieve).toHaveBeenCalledWith({
lookup: experiment.experiment_id,
- })
+ }),
);
});
test("Hostname filter only shows compute resources that are configured in a GRP", async () => {
// Service call mocks
services.ApplicationInterfaceService.list.mockResolvedValue([]);
- services.ExperimentStatisticsService.get.mockResolvedValue(
- new utils.PaginationIterator(
- {
- count: 0,
- next: null,
- previous: null,
- results: {
- all_experiment_count: 0,
- completed_experiment_count: 0,
- cancelled_experiment_count: 0,
- failed_experiment_count: 0,
- created_experiment_count: 0,
- running_experiment_count: 0,
- all_experiments: [],
- completed_experiments: [],
- failed_experiments: [],
- cancelled_experiments: [],
- created_experiments: [],
- running_experiments: [],
- },
- limit: 50,
- offset: 0,
- },
- models.ExperimentStatistics
- )
+ services.ExperimentStatisticsService.get.mockImplementation(() =>
+ Promise.resolve(emptyStatsPaginator()),
);
services.ComputeResourceService.namesList.mockResolvedValue([
{ host_id: "compute4-abcd", host: "d-compute4" },
@@ -261,7 +258,7 @@ test("Hostname filter only shows compute resources that are configured in a GRP"
// The render method returns a collection of utilities to query your component.
const { findByText } = renderWithProviders(ExperimentStatisticsContainer);
- const addFiltersMenu = await findByText("Add Filters");
+ const addFiltersMenu = await findByText("Add filter");
await fireEvent.click(addFiltersMenu);
@@ -269,14 +266,12 @@ test("Hostname filter only shows compute resources that are configured in a GRP"
await fireEvent.click(hostnameMenuItem);
- const computeResourcesSelect = await findByText(
- "Select compute resource to filter on"
- );
+ const computeResourcesSelect = await findByText("Compute resource");
const options = computeResourcesSelect.parentElement.options;
expect(options.length).toBe(4);
- // option 0 is the null one ("Select compute resource to filter on")
+ // option 0 is the null one ("Compute resource")
// verify that options 1-3 are compute resources 1, 3, 4. That is, verify that
// filtering worked and that they were sorted.
expect(options[1].value).toBe("compute1-abcd");
diff --git a/airavata-django-portal/django_airavata/static/common/css/app.css b/airavata-django-portal/django_airavata/static/common/css/app.css
index b375f4023..88f0e3b51 100644
--- a/airavata-django-portal/django_airavata/static/common/css/app.css
+++ b/airavata-django-portal/django_airavata/static/common/css/app.css
@@ -138,3 +138,36 @@
@apply bg-background text-foreground;
}
}
+
+/*
+ * Unovis chart theming for the shadcn-vue chart components (ui/chart,
+ * ui/chart-area). Wire Unovis' CSS variables to the design tokens and size the
+ * axis/tooltip fonts to the design-system scale (text-xs, 0.75rem) so charts
+ * read consistently with the rest of the UI. The tooltip itself is rendered by
+ * our ChartTooltip (a Card), so Unovis' default tooltip chrome is neutralized.
+ */
+:root {
+ --vis-font-family: inherit;
+ --vis-axis-tick-label-font-size: 0.75rem;
+ --vis-axis-tick-label-color: var(--muted-foreground);
+ --vis-axis-tick-color: var(--border);
+ --vis-text-color: var(--muted-foreground);
+ --vis-primary-color: var(--primary);
+ --vis-secondary-color: var(--muted-foreground);
+ --vis-crosshair-line-stroke-color: var(--muted-foreground);
+ --vis-crosshair-circle-stroke-color: var(--background);
+
+ --vis-tooltip-background-color: none;
+ --vis-tooltip-border-color: none;
+ --vis-tooltip-text-color: none;
+ --vis-tooltip-shadow-color: none;
+ --vis-tooltip-backdrop-filter: none;
+ --vis-tooltip-padding: none;
+}
+
+/* Let Unovis' XY container fill its parent. Without this it falls back to a
+ fixed ~300px width, collapsing the area chart into a narrow box. */
+.unovis-xy-container {
+ width: 100%;
+ height: 100%;
+}
diff --git a/airavata-django-portal/django_airavata/static/common/js/components/ui/chart-area/AreaChart.vue b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart-area/AreaChart.vue
new file mode 100644
index 000000000..e4beceb30
--- /dev/null
+++ b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart-area/AreaChart.vue
@@ -0,0 +1,173 @@
+<script setup>
+import { Area, Axis, CurveType, Line } from "@unovis/ts";
+import { VisArea, VisAxis, VisLine, VisXYContainer } from "@unovis/vue";
+import { useMounted } from "@vueuse/core";
+import { useId } from "reka-ui";
+import { computed, ref } from "vue";
+import { cn } from "@/lib/utils";
+import { ChartCrosshair, ChartLegend, defaultColors } from "../chart";
+
+const props = defineProps({
+ // The source data, where each entry is a dictionary.
+ data: { type: Array, required: true },
+ // Categories selected from the data; populate the legend and tooltip.
+ categories: { type: Array, required: true },
+ // The key used to map the data to the x axis.
+ index: { type: String, required: true },
+ // Override the default colors.
+ colors: { type: Array, required: false },
+ // Margin of the container.
+ margin: {
+ type: Object,
+ required: false,
+ default: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
+ },
+ // Opacity of non-selected series.
+ filterOpacity: { type: Number, required: false, default: 0.2 },
+ // Function to format the X tick label.
+ xFormatter: { type: Function, required: false },
+ // Function to format the Y tick label.
+ yFormatter: { type: Function, required: false },
+ showXAxis: { type: Boolean, required: false, default: true },
+ showYAxis: { type: Boolean, required: false, default: true },
+ showTooltip: { type: Boolean, required: false, default: true },
+ showLegend: { type: Boolean, required: false, default: true },
+ showGridLine: { type: Boolean, required: false, default: true },
+ // Render a custom tooltip component.
+ customTooltip: { type: null, required: false },
+ // Curve interpolation type. Defaults to a smooth monotone curve.
+ curveType: { type: String, required: false, default: CurveType.MonotoneX },
+ // Controls the visibility of the translucent area gradient fill.
+ showGradient: { type: Boolean, required: false, default: true },
+});
+
+const emits = defineEmits(["legendItemClick"]);
+
+const chartRef = useId();
+
+const index = computed(() => props.index);
+const colors = computed(() =>
+ props.colors?.length ? props.colors : defaultColors(props.categories.length),
+);
+
+const legendItems = ref(
+ props.categories.map((category, i) => ({
+ name: category,
+ color: colors.value[i],
+ inactive: false,
+ })),
+);
+
+const isMounted = useMounted();
+
+function handleLegendItemClick(d, i) {
+ emits("legendItemClick", d, i);
+}
+</script>
+
+<template>
+ <div :class="cn('flex h-full w-full flex-col items-end', $attrs.class ?? '')">
+ <ChartLegend
+ v-if="showLegend"
+ v-model:items="legendItems"
+ @legend-item-click="handleLegendItemClick"
+ />
+
+ <VisXYContainer
+ :style="{ height: isMounted ? '100%' : 'auto' }"
+ :margin="{ left: 20, right: 20 }"
+ :data="data"
+ >
+ <svg width="0" height="0">
+ <defs>
+ <linearGradient
+ v-for="(color, i) in colors"
+ :id="`${chartRef}-color-${i}`"
+ :key="i"
+ x1="0"
+ y1="0"
+ x2="0"
+ y2="1"
+ >
+ <template v-if="showGradient">
+ <stop offset="5%" :stop-color="color" stop-opacity="0.4" />
+ <stop offset="95%" :stop-color="color" stop-opacity="0" />
+ </template>
+ <template v-else>
+ <stop offset="0%" :stop-color="color" />
+ </template>
+ </linearGradient>
+ </defs>
+ </svg>
+
+ <ChartCrosshair
+ v-if="showTooltip"
+ :colors="colors"
+ :items="legendItems"
+ :index="index"
+ :custom-tooltip="customTooltip"
+ />
+
+ <template v-for="(category, i) in categories" :key="category">
+ <VisArea
+ :x="(d, i) => i"
+ :y="(d) => d[category]"
+ color="auto"
+ :curve-type="curveType"
+ :attributes="{
+ [Area.selectors.area]: {
+ fill: `url(#${chartRef}-color-${i})`,
+ },
+ }"
+ :opacity="
+ legendItems.find((item) => item.name === category)?.inactive
+ ? filterOpacity
+ : 1
+ "
+ />
+ </template>
+
+ <template v-for="(category, i) in categories" :key="category">
+ <VisLine
+ :x="(d, i) => i"
+ :y="(d) => d[category]"
+ :color="colors[i]"
+ :curve-type="curveType"
+ :attributes="{
+ [Line.selectors.line]: {
+ opacity: legendItems.find((item) => item.name === category)
+ ?.inactive
+ ? filterOpacity
+ : 1,
+ },
+ }"
+ />
+ </template>
+
+ <VisAxis
+ v-if="showXAxis"
+ type="x"
+ :tick-format="xFormatter ?? ((v) => data[v]?.[index])"
+ :grid-line="false"
+ :tick-line="false"
+ tick-text-color="var(--muted-foreground)"
+ />
+ <VisAxis
+ v-if="showYAxis"
+ type="y"
+ :tick-line="false"
+ :tick-format="yFormatter"
+ :domain-line="false"
+ :grid-line="showGridLine"
+ :attributes="{
+ [Axis.selectors.grid]: {
+ class: 'text-muted',
+ },
+ }"
+ tick-text-color="var(--muted-foreground)"
+ />
+
+ <slot />
+ </VisXYContainer>
+ </div>
+</template>
diff --git a/airavata-django-portal/django_airavata/static/common/js/components/ui/chart-area/index.js b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart-area/index.js
new file mode 100644
index 000000000..38d8fa6d3
--- /dev/null
+++ b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart-area/index.js
@@ -0,0 +1,4 @@
+export { default as AreaChart } from "./AreaChart.vue";
+// Re-export the curve-type enum so consumers can pick a curve without taking a
+// direct dependency on @unovis (which is installed in this common package).
+export { CurveType } from "@unovis/ts";
diff --git a/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartCrosshair.vue b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartCrosshair.vue
new file mode 100644
index 000000000..ea7bc8014
--- /dev/null
+++ b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartCrosshair.vue
@@ -0,0 +1,45 @@
+<script setup>
+import { omit } from "@unovis/ts";
+import { VisCrosshair, VisTooltip } from "@unovis/vue";
+import { createApp } from "vue";
+import { ChartTooltip } from ".";
+
+const props = defineProps({
+ colors: { type: Array, required: false, default: () => [] },
+ index: { type: String, required: true },
+ items: { type: Array, required: true },
+ customTooltip: { type: null, required: false },
+});
+
+// Use a WeakMap to cache the rendered tooltip HTML per datapoint.
+const wm = new WeakMap();
+function template(d) {
+ if (wm.has(d)) {
+ return wm.get(d);
+ } else {
+ const componentDiv = document.createElement("div");
+ const omittedData = Object.entries(omit(d, [props.index])).map(
+ ([key, value]) => {
+ const legendReference = props.items.find((i) => i.name === key);
+ return { ...legendReference, value };
+ },
+ );
+ const TooltipComponent = props.customTooltip ?? ChartTooltip;
+ createApp(TooltipComponent, {
+ title: d[props.index].toString(),
+ data: omittedData,
+ }).mount(componentDiv);
+ wm.set(d, componentDiv.innerHTML);
+ return componentDiv.innerHTML;
+ }
+}
+
+function color(d, i) {
+ return props.colors[i] ?? "transparent";
+}
+</script>
+
+<template>
+ <VisTooltip :horizontal-shift="20" :vertical-shift="20" />
+ <VisCrosshair :template="template" :color="color" />
+</template>
diff --git a/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartLegend.vue b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartLegend.vue
new file mode 100644
index 000000000..85fd92887
--- /dev/null
+++ b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartLegend.vue
@@ -0,0 +1,65 @@
+<script setup>
+import { BulletLegend } from "@unovis/ts";
+import { VisBulletLegend } from "@unovis/vue";
+import { nextTick, onMounted, ref } from "vue";
+import { buttonVariants } from "../button";
+
+const props = defineProps({
+ items: { type: Array, required: false, default: () => [] },
+});
+
+const emits = defineEmits(["legendItemClick", "update:items"]);
+
+const elRef = ref();
+
+function keepStyling() {
+ const selector = `.${BulletLegend.selectors.item}`;
+ nextTick(() => {
+ const elements = elRef.value?.querySelectorAll(selector);
+ const classes = buttonVariants({ variant: "ghost", size: "sm" }).split(" ");
+ elements?.forEach((el) =>
+ el.classList.add(...classes, "!inline-flex", "!mr-2"),
+ );
+ });
+}
+
+onMounted(() => {
+ keepStyling();
+});
+
+function onLegendItemClick(d, i) {
+ emits("legendItemClick", d, i);
+ const isBulletActive = !props.items[i].inactive;
+ const isFilterApplied = props.items.some((i) => i.inactive);
+ if (isFilterApplied && isBulletActive) {
+ // reset filter
+ emits(
+ "update:items",
+ props.items.map((item) => ({ ...item, inactive: false })),
+ );
+ } else {
+ // apply selection, set other item as inactive
+ emits(
+ "update:items",
+ props.items.map((item) =>
+ item.name === d.name
+ ? { ...d, inactive: false }
+ : { ...item, inactive: true },
+ ),
+ );
+ }
+ keepStyling();
+}
+</script>
+
+<template>
+ <div
+ ref="elRef"
+ class="w-max"
+ :style="{
+ '--vis-legend-bullet-size': '16px',
+ }"
+ >
+ <VisBulletLegend :items="items" :on-legend-item-click="onLegendItemClick" />
+ </div>
+</template>
diff --git a/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartSingleTooltip.vue b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartSingleTooltip.vue
new file mode 100644
index 000000000..472eba7c9
--- /dev/null
+++ b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartSingleTooltip.vue
@@ -0,0 +1,73 @@
+<script setup>
+import { omit } from "@unovis/ts";
+import { VisTooltip } from "@unovis/vue";
+import { createApp } from "vue";
+import { ChartTooltip } from ".";
+
+const props = defineProps({
+ selector: { type: String, required: true },
+ index: { type: String, required: true },
+ items: { type: Array, required: false },
+ valueFormatter: { type: Function, required: false },
+ customTooltip: { type: null, required: false },
+});
+
+// Use a WeakMap to cache the rendered tooltip HTML per datapoint.
+const wm = new WeakMap();
+function template(d, i, elements) {
+ const valueFormatter = props.valueFormatter ?? ((tick) => `${tick}`);
+ if (props.index in d) {
+ if (wm.has(d)) {
+ return wm.get(d);
+ } else {
+ const componentDiv = document.createElement("div");
+ const omittedData = Object.entries(omit(d, [props.index])).map(
+ ([key, value]) => {
+ const legendReference = props.items?.find((i) => i.name === key);
+ return { ...legendReference, value: valueFormatter(value) };
+ },
+ );
+ const TooltipComponent = props.customTooltip ?? ChartTooltip;
+ createApp(TooltipComponent, {
+ title: d[props.index],
+ data: omittedData,
+ }).mount(componentDiv);
+ wm.set(d, componentDiv.innerHTML);
+ return componentDiv.innerHTML;
+ }
+ } else {
+ const data = d.data;
+
+ if (wm.has(data)) {
+ return wm.get(data);
+ } else {
+ const style = getComputedStyle(elements[i]);
+ const omittedData = [
+ {
+ name: data.name,
+ value: valueFormatter(data[props.index]),
+ color: style.fill,
+ },
+ ];
+ const componentDiv = document.createElement("div");
+ const TooltipComponent = props.customTooltip ?? ChartTooltip;
+ createApp(TooltipComponent, {
+ title: d[props.index],
+ data: omittedData,
+ }).mount(componentDiv);
+ wm.set(d, componentDiv.innerHTML);
+ return componentDiv.innerHTML;
+ }
+ }
+}
+</script>
+
+<template>
+ <VisTooltip
+ :horizontal-shift="20"
+ :vertical-shift="20"
+ :triggers="{
+ [selector]: template,
+ }"
+ />
+</template>
diff --git a/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartTooltip.vue b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartTooltip.vue
new file mode 100644
index 000000000..ac98364c9
--- /dev/null
+++ b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/ChartTooltip.vue
@@ -0,0 +1,36 @@
+<script setup>
+import { Card, CardContent, CardHeader, CardTitle } from "../card";
+
+defineProps({
+ title: { type: String, required: false },
+ data: { type: Array, required: true },
+});
+</script>
+
+<template>
+ <Card class="text-sm">
+ <CardHeader v-if="title" class="border-b p-3">
+ <CardTitle>
+ {{ title }}
+ </CardTitle>
+ </CardHeader>
+ <CardContent class="flex min-w-[180px] flex-col gap-1 p-3">
+ <div v-for="(item, key) in data" :key="key" class="flex justify-between">
+ <div class="flex items-center">
+ <span class="mr-2 h-2.5 w-2.5">
+ <svg width="100%" height="100%" viewBox="0 0 30 30">
+ <path
+ d=" M 15 15 m -14, 0 a 14,14 0 1,1 28,0 a 14,14 0 1,1 -28,0"
+ :stroke="item.color"
+ :fill="item.color"
+ stroke-width="1"
+ />
+ </svg>
+ </span>
+ <span>{{ item.name }}</span>
+ </div>
+ <span class="ml-4 font-semibold">{{ item.value }}</span>
+ </div>
+ </CardContent>
+ </Card>
+</template>
diff --git a/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/index.js b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/index.js
new file mode 100644
index 000000000..b626f6d53
--- /dev/null
+++ b/airavata-django-portal/django_airavata/static/common/js/components/ui/chart/index.js
@@ -0,0 +1,24 @@
+// Vendored from shadcn-vue's chart component family (the @unovis-based
+// AreaChart/LineChart helpers), converted from TypeScript to the plain-JS
+// <script setup> convention this repo uses for its ui/ components.
+export { default as ChartCrosshair } from "./ChartCrosshair.vue";
+export { default as ChartLegend } from "./ChartLegend.vue";
+export { default as ChartSingleTooltip } from "./ChartSingleTooltip.vue";
+export { default as ChartTooltip } from "./ChartTooltip.vue";
+
+export function defaultColors(count = 3) {
+ const quotient = Math.floor(count / 2);
+ const remainder = count % 2;
+
+ const primaryCount = quotient + remainder;
+ const secondaryCount = quotient;
+ return [
+ ...Array.from(new Array(primaryCount).keys()).map(
+ (i) => `hsl(var(--vis-primary-color) / ${1 - (1 / primaryCount) * i})`,
+ ),
+ ...Array.from(new Array(secondaryCount).keys()).map(
+ (i) =>
+ `hsl(var(--vis-secondary-color) / ${1 - (1 / secondaryCount) * i})`,
+ ),
+ ];
+}
diff --git a/airavata-django-portal/django_airavata/static/common/js/components/ui/index.js b/airavata-django-portal/django_airavata/static/common/js/components/ui/index.js
index ebc654b99..91717a29f 100644
--- a/airavata-django-portal/django_airavata/static/common/js/components/ui/index.js
+++ b/airavata-django-portal/django_airavata/static/common/js/components/ui/index.js
@@ -3,6 +3,8 @@ export * from "./alert";
export * from "./badge";
export * from "./button";
export * from "./card";
+export * from "./chart";
+export * from "./chart-area";
export * from "./checkbox";
export * from "./dialog";
export * from "./dropdown-menu";
diff --git a/airavata-django-portal/django_airavata/static/common/package.json b/airavata-django-portal/django_airavata/static/common/package.json
index 40fd171b2..977724e84 100644
--- a/airavata-django-portal/django_airavata/static/common/package.json
+++ b/airavata-django-portal/django_airavata/static/common/package.json
@@ -17,6 +17,8 @@
"dependencies": {
"@lucide/vue": "^1.18.0",
"@tanstack/vue-table": "^8.21.3",
+ "@unovis/ts": "^1.6.5",
+ "@unovis/vue": "^1.6.5",
"@uppy/core": "^5.2.0",
"@uppy/drag-drop": "^5.1.0",
"@uppy/status-bar": "^5.1.0",
diff --git a/airavata-django-portal/django_airavata/static/common/yarn.lock b/airavata-django-portal/django_airavata/static/common/yarn.lock
index d848f05b1..abb603c48 100644
--- a/airavata-django-portal/django_airavata/static/common/yarn.lock
+++ b/airavata-django-portal/django_airavata/static/common/yarn.lock
@@ -2,6 +2,39 @@
# yarn lockfile v1
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7"
+ integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.29.7"
+ js-tokens "^4.0.0"
+ picocolors "^1.1.1"
+
+"@babel/generator@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.7.tgz#cca0b8827e6bcf3ba176788e7f3b180ad6db2fa3"
+ integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==
+ dependencies:
+ "@babel/parser" "^7.29.7"
+ "@babel/types" "^7.29.7"
+ "@jridgewell/gen-mapping" "^0.3.12"
+ "@jridgewell/trace-mapping" "^0.3.28"
+ jsesc "^3.0.2"
+
+"@babel/helper-globals@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz#f04a96fbd8473241b1079243f5b3f03a3010ab7b"
+ integrity sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==
+
+"@babel/helper-module-imports@^7.16.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz#ef25048a518e828d7393fac5882ddd73921d7396"
+ integrity sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==
+ dependencies:
+ "@babel/traverse" "^7.29.7"
+ "@babel/types" "^7.29.7"
+
"@babel/helper-string-parser@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz#7f0871d99824d23137d60f86fcf6130fd5a1b51f"
@@ -19,6 +52,33 @@
dependencies:
"@babel/types" "^7.29.7"
+"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.7.tgz#12022450c45a4da6d8d8287b18a4ff2ddb23f768"
+ integrity sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==
+
+"@babel/template@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.29.7.tgz#4d9d4004f645cdd304de958c725162784ecac700"
+ integrity sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==
+ dependencies:
+ "@babel/code-frame" "^7.29.7"
+ "@babel/parser" "^7.29.7"
+ "@babel/types" "^7.29.7"
+
+"@babel/traverse@^7.29.7":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.7.tgz#c47b07a41b95da0907d026b5dd894d98de7d2f2d"
+ integrity sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==
+ dependencies:
+ "@babel/code-frame" "^7.29.7"
+ "@babel/generator" "^7.29.7"
+ "@babel/helper-globals" "^7.29.7"
+ "@babel/parser" "^7.29.7"
+ "@babel/template" "^7.29.7"
+ "@babel/types" "^7.29.7"
+ debug "^4.3.1"
+
"@babel/types@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92"
@@ -71,6 +131,86 @@
dependencies:
tslib "^2.4.0"
+"@emotion/babel-plugin@^11.13.5":
+ version "11.13.5"
+ resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz#eab8d65dbded74e0ecfd28dc218e75607c4e7bc0"
+ integrity sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/runtime" "^7.18.3"
+ "@emotion/hash" "^0.9.2"
+ "@emotion/memoize" "^0.9.0"
+ "@emotion/serialize" "^1.3.3"
+ babel-plugin-macros "^3.1.0"
+ convert-source-map "^1.5.0"
+ escape-string-regexp "^4.0.0"
+ find-root "^1.1.0"
+ source-map "^0.5.7"
+ stylis "4.2.0"
+
+"@emotion/cache@^11.13.5":
+ version "11.14.0"
+ resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.14.0.tgz#ee44b26986eeb93c8be82bb92f1f7a9b21b2ed76"
+ integrity sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==
+ dependencies:
+ "@emotion/memoize" "^0.9.0"
+ "@emotion/sheet" "^1.4.0"
+ "@emotion/utils" "^1.4.2"
+ "@emotion/weak-memoize" "^0.4.0"
+ stylis "4.2.0"
+
+"@emotion/css@^11.7.1":
+ version "11.13.5"
+ resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.13.5.tgz#db2d3be6780293640c082848e728a50544b9dfa4"
+ integrity sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==
+ dependencies:
+ "@emotion/babel-plugin" "^11.13.5"
+ "@emotion/cache" "^11.13.5"
+ "@emotion/serialize" "^1.3.3"
+ "@emotion/sheet" "^1.4.0"
+ "@emotion/utils" "^1.4.2"
+
+"@emotion/hash@^0.9.2":
+ version "0.9.2"
+ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b"
+ integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==
+
+"@emotion/memoize@^0.9.0":
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102"
+ integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==
+
+"@emotion/serialize@^1.3.3":
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.3.tgz#d291531005f17d704d0463a032fe679f376509e8"
+ integrity sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==
+ dependencies:
+ "@emotion/hash" "^0.9.2"
+ "@emotion/memoize" "^0.9.0"
+ "@emotion/unitless" "^0.10.0"
+ "@emotion/utils" "^1.4.2"
+ csstype "^3.0.2"
+
+"@emotion/sheet@^1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c"
+ integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==
+
+"@emotion/unitless@^0.10.0":
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745"
+ integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==
+
+"@emotion/utils@^1.4.2":
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.2.tgz#6df6c45881fcb1c412d6688a311a98b7f59c1b52"
+ integrity sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==
+
+"@emotion/weak-memoize@^0.4.0":
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6"
+ integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==
+
"@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.8.0":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595"
@@ -198,7 +338,7 @@
dependencies:
"@swc/helpers" "^0.5.0"
-"@jridgewell/gen-mapping@^0.3.5":
+"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
version "0.3.13"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
@@ -224,7 +364,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
-"@jridgewell/trace-mapping@^0.3.24":
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
version "0.3.31"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
@@ -232,11 +372,66 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
+"@juggle/resize-observer@^3.3.1":
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
+ integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
+
"@lucide/vue@^1.18.0":
version "1.18.0"
resolved "https://registry.yarnpkg.com/@lucide/vue/-/vue-1.18.0.tgz#57bcc6cfe4f13c203a827f3794baec3f11fbf732"
integrity sha512-DmnUpDB85PlMZ+ofjZLcKq3JoJnaD1bk7SIj9xwUvqerfNqA6hCLa0/m3gIybH6rdrErABbqvTD8yYJdNqiZ3Q==
+"@mapbox/geojson-rewind@^0.5.2":
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz#591a5d71a9cd1da1a0bf3420b3bea31b0fc7946a"
+ integrity sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==
+ dependencies:
+ get-stream "^6.0.1"
+ minimist "^1.2.6"
+
+"@mapbox/jsonlint-lines-primitives@^2.0.2":
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234"
+ integrity sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==
+
+"@mapbox/mapbox-gl-supported@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz#c15367178d8bfe4765e6b47b542fe821ce259c7b"
+ integrity sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==
+
+"@mapbox/point-geometry@*":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz#3328fb54b3a1273bc619bf0a6baad8de37181749"
+ integrity sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==
+
+"@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0":
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2"
+ integrity sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==
+
+"@mapbox/tiny-sdf@^2.0.5":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz#95dae0dc371b8c55c53d70f3e25d56823427f4c8"
+ integrity sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==
+
+"@mapbox/unitbezier@^0.0.1":
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz#d32deb66c7177e9e9dfc3bbd697083e2e657ff01"
+ integrity sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==
+
+"@mapbox/vector-tile@^1.3.1":
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz#d3a74c90402d06e89ec66de49ec817ff53409666"
+ integrity sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==
+ dependencies:
+ "@mapbox/point-geometry" "~0.1.0"
+
+"@mapbox/whoots-js@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
+ integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
+
"@napi-rs/wasm-runtime@^1.1.4":
version "1.1.5"
resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz#cccd6ebc40b991dea6936f9126b1b8155b6c4c95"
@@ -483,6 +678,245 @@
dependencies:
tslib "^2.4.0"
+"@types/d3-array@*", "@types/d3-array@~3.2.2":
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.2.tgz#e02151464d02d4a1b44646d0fcdb93faf88fde8c"
+ integrity sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==
+
+"@types/d3-axis@*", "@types/d3-axis@~3.0.6":
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-3.0.6.tgz#e760e5765b8188b1defa32bc8bb6062f81e4c795"
+ integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==
+ dependencies:
+ "@types/d3-selection" "*"
+
+"@types/d3-brush@*", "@types/d3-brush@~3.0.6":
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-3.0.6.tgz#c2f4362b045d472e1b186cdbec329ba52bdaee6c"
+ integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==
+ dependencies:
+ "@types/d3-selection" "*"
+
+"@types/d3-chord@*", "@types/d3-chord@~3.0.6":
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-3.0.6.tgz#1706ca40cf7ea59a0add8f4456efff8f8775793d"
+ integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==
+
+"@types/d3-collection@^1.0.10":
+ version "1.0.13"
+ resolved "https://registry.yarnpkg.com/@types/d3-collection/-/d3-collection-1.0.13.tgz#4bd051f698515963a9a7c925c04aaf0acd3902f8"
+ integrity sha512-v0Rgw3IZebRyamcwVmtTDCZ8OmQcj4siaYjNc7wGMZT7PmdSHawGsCOQMxyLvZ7lWjfohYLK0oXtilMOMgfY8A==
+
+"@types/d3-color@*", "@types/d3-color@~3.1.3":
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2"
+ integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
+
+"@types/d3-contour@*":
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-3.0.6.tgz#9ada3fa9c4d00e3a5093fed0356c7ab929604231"
+ integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==
+ dependencies:
+ "@types/d3-array" "*"
+ "@types/geojson" "*"
+
+"@types/d3-delaunay@*":
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz#185c1a80cc807fdda2a3fe960f7c11c4a27952e1"
+ integrity sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==
+
+"@types/d3-dispatch@*":
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz#ef004d8a128046cfce434d17182f834e44ef95b2"
+ integrity sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==
+
+"@types/d3-drag@*", "@types/d3-drag@~3.0.7":
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02"
+ integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==
+ dependencies:
+ "@types/d3-selection" "*"
+
+"@types/d3-dsv@*":
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz#0a351f996dc99b37f4fa58b492c2d1c04e3dac17"
+ integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==
+
+"@types/d3-ease@*", "@types/d3-ease@~3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b"
+ integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==
+
+"@types/d3-fetch@*":
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz#c04a2b4f23181aa376f30af0283dbc7b3b569980"
+ integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==
+ dependencies:
+ "@types/d3-dsv" "*"
+
+"@types/d3-force@*", "@types/d3-force@~3.0.10":
+ version "3.0.10"
+ resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-3.0.10.tgz#6dc8fc6e1f35704f3b057090beeeb7ac674bff1a"
+ integrity sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==
+
+"@types/d3-format@*":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90"
+ integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==
+
+"@types/d3-geo@*", "@types/d3-geo@~3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-3.1.0.tgz#b9e56a079449174f0a2c8684a9a4df3f60522440"
+ integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==
+ dependencies:
+ "@types/geojson" "*"
+
+"@types/d3-hierarchy@*", "@types/d3-hierarchy@~3.1.7":
+ version "3.1.7"
+ resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz#6023fb3b2d463229f2d680f9ac4b47466f71f17b"
+ integrity sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==
+
+"@types/d3-interpolate@*", "@types/d3-interpolate@~3.0.4":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c"
+ integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==
+ dependencies:
+ "@types/d3-color" "*"
+
+"@types/d3-path@*", "@types/d3-path@~3.1.1":
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.1.tgz#f632b380c3aca1dba8e34aa049bcd6a4af23df8a"
+ integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==
+
+"@types/d3-path@^1":
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.11.tgz#45420fee2d93387083b34eae4fe6d996edf482bc"
+ integrity sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==
+
+"@types/d3-polygon@*":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz#dfae54a6d35d19e76ac9565bcb32a8e54693189c"
+ integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==
+
+"@types/d3-quadtree@*":
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz#d4740b0fe35b1c58b66e1488f4e7ed02952f570f"
+ integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==
+
+"@types/d3-random@*":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-3.0.3.tgz#ed995c71ecb15e0cd31e22d9d5d23942e3300cfb"
+ integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==
+
+"@types/d3-sankey@^0.12.4":
+ version "0.12.5"
+ resolved "https://registry.yarnpkg.com/@types/d3-sankey/-/d3-sankey-0.12.5.tgz#9b79ff5768250cfd8c6340b167c409a0374f40bd"
+ integrity sha512-/3RZSew0cLAtzGQ+C89hq/Rp3H20QJuVRSqFy6RKLe7E0B8kd2iOS1oBsodrgds4PcNVpqWhdUEng/SHvBcJ6Q==
+ dependencies:
+ "@types/d3-shape" "^1"
+
+"@types/d3-scale-chromatic@*":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#dc6d4f9a98376f18ea50bad6c39537f1b5463c39"
+ integrity sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==
+
+"@types/d3-scale@*", "@types/d3-scale@~4.0.9":
+ version "4.0.9"
+ resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.9.tgz#57a2f707242e6fe1de81ad7bfcccaaf606179afb"
+ integrity sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==
+ dependencies:
+ "@types/d3-time" "*"
+
+"@types/d3-selection@*", "@types/d3-selection@~3.0.0":
+ version "3.0.11"
+ resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.11.tgz#bd7a45fc0a8c3167a631675e61bc2ca2b058d4a3"
+ integrity sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==
+
+"@types/d3-shape@*", "@types/d3-shape@~3.1.7":
+ version "3.1.8"
+ resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.8.tgz#d1516cc508753be06852cd06758e3bb54a22b0e3"
+ integrity sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==
+ dependencies:
+ "@types/d3-path" "*"
+
+"@types/d3-shape@^1":
+ version "1.3.12"
+ resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.12.tgz#8f2f9f7a12e631ce6700d6d55b84795ce2c8b259"
+ integrity sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==
+ dependencies:
+ "@types/d3-path" "^1"
+
+"@types/d3-time-format@*":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2"
+ integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==
+
+"@types/d3-time@*":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.4.tgz#8472feecd639691450dd8000eb33edd444e1323f"
+ integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==
+
+"@types/d3-timer@*", "@types/d3-timer@~3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70"
+ integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==
+
+"@types/d3-transition@*", "@types/d3-transition@~3.0.9":
+ version "3.0.9"
+ resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.9.tgz#1136bc57e9ddb3c390dccc9b5ff3b7d2b8d94706"
+ integrity sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==
+ dependencies:
+ "@types/d3-selection" "*"
+
+"@types/d3-zoom@*", "@types/d3-zoom@~3.0.8":
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b"
+ integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==
+ dependencies:
+ "@types/d3-interpolate" "*"
+ "@types/d3-selection" "*"
+
+"@types/d3@^7.4.0":
+ version "7.4.3"
+ resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.4.3.tgz#d4550a85d08f4978faf0a4c36b848c61eaac07e2"
+ integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==
+ dependencies:
+ "@types/d3-array" "*"
+ "@types/d3-axis" "*"
+ "@types/d3-brush" "*"
+ "@types/d3-chord" "*"
+ "@types/d3-color" "*"
+ "@types/d3-contour" "*"
+ "@types/d3-delaunay" "*"
+ "@types/d3-dispatch" "*"
+ "@types/d3-drag" "*"
+ "@types/d3-dsv" "*"
+ "@types/d3-ease" "*"
+ "@types/d3-fetch" "*"
+ "@types/d3-force" "*"
+ "@types/d3-format" "*"
+ "@types/d3-geo" "*"
+ "@types/d3-hierarchy" "*"
+ "@types/d3-interpolate" "*"
+ "@types/d3-path" "*"
+ "@types/d3-polygon" "*"
+ "@types/d3-quadtree" "*"
+ "@types/d3-random" "*"
+ "@types/d3-scale" "*"
+ "@types/d3-scale-chromatic" "*"
+ "@types/d3-selection" "*"
+ "@types/d3-shape" "*"
+ "@types/d3-time" "*"
+ "@types/d3-time-format" "*"
+ "@types/d3-timer" "*"
+ "@types/d3-transition" "*"
+ "@types/d3-zoom" "*"
+
+"@types/dagre@^0.7.50":
+ version "0.7.54"
+ resolved "https://registry.yarnpkg.com/@types/dagre/-/dagre-0.7.54.tgz#4f3be2a3f89938b0483013739362a477ec978a84"
+ integrity sha512-QjcRY+adGbYvBFS7cwv5txhVIwX1XXIUswWl+kSQTbI6NjgZydrZkEKX/etzVd7i+bCsCb40Z/xlBY5eoFuvWQ==
+
"@types/esrecurse@^4.3.1":
version "4.3.1"
resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec"
@@ -493,21 +927,217 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24"
integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==
+"@types/geojson@*", "@types/geojson@^7946.0.10", "@types/geojson@^7946.0.8":
+ version "7946.0.16"
+ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
+ integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==
+
"@types/json-schema@^7.0.15":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+"@types/leaflet@1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.7.6.tgz#6580f4babb648972c5af3abc3d66866753fa9311"
+ integrity sha512-Emkz3V08QnlelSbpT46OEAx+TBZYTOX2r1yM7W+hWg5+djHtQ1GbEXBDRLaqQDOYcDI51Ss0ayoqoKD4CtLUDA==
+ dependencies:
+ "@types/geojson" "*"
+
+"@types/mapbox__point-geometry@*":
+ version "1.0.87"
+ resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-1.0.87.tgz#164ba7a9f2cd899e7fa5086f6bb6abd158241fdd"
+ integrity sha512-32whuSWD/aDBHpRrZmEMlRjxR0L3sSYUaRnVmw0XwyVMvuU5LsRqrFwYud7gXjqmUE0MSoJB91+IJMqCyYyxag==
+ dependencies:
+ "@mapbox/point-geometry" "*"
+
+"@types/mapbox__point-geometry@^0.1.2":
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz#0ef017b75eedce02ff6243b4189210e2e6d5e56d"
+ integrity sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==
+
+"@types/mapbox__vector-tile@^1.3.0":
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz#ad757441ef1d34628d9e098afd9c91423c1f8734"
+ integrity sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==
+ dependencies:
+ "@types/geojson" "*"
+ "@types/mapbox__point-geometry" "*"
+ "@types/pbf" "*"
+
+"@types/parse-json@^4.0.0":
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239"
+ integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==
+
+"@types/pbf@*", "@types/pbf@^3.0.2":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.5.tgz#a9495a58d8c75be4ffe9a0bd749a307715c07404"
+ integrity sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==
+
"@types/retry@0.12.2":
version "0.12.2"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a"
integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==
+"@types/supercluster@^5.0.2":
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/@types/supercluster/-/supercluster-5.0.3.tgz#aa03a77c6545265e63b50fa267ab12afe0c27658"
+ integrity sha512-XMSqQEr7YDuNtFwSgaHHOjsbi0ZGL62V9Js4CW45RBuRYlNWSW/KDqN+RFFE7HdHcGhJPtN0klKvw06r9Kg7rg==
+ dependencies:
+ "@types/geojson" "*"
+
+"@types/three@^0.135.0":
+ version "0.135.0"
+ resolved "https://registry.yarnpkg.com/@types/three/-/three-0.135.0.tgz#db7f9d9699b60aba844fc5b2893adf6dd6811665"
+ integrity sha512-l7WLhIHjhHMtlpyTSltPPAKLpiMwgMD1hXHj59AVUpYRoZP7Fd9NNOSRSvZBCPLpTHPYojgQvSJCoza9zoL7bg==
+
+"@types/throttle-debounce@^5.0.0":
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-5.0.2.tgz#3489d91673a4be830c2c9e2acf1f6cdab724102c"
+ integrity sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==
+
+"@types/topojson-client@*", "@types/topojson-client@^3.0.0":
+ version "3.1.5"
+ resolved "https://registry.yarnpkg.com/@types/topojson-client/-/topojson-client-3.1.5.tgz#3fdbcd52161db8747a071b1d0f635ac700cf599d"
+ integrity sha512-C79rySTyPxnQNNguTZNI1Ct4D7IXgvyAs3p9HPecnl6mNrJ5+UhvGNYcZfpROYV2lMHI48kJPxwR+F9C6c7nmw==
+ dependencies:
+ "@types/geojson" "*"
+ "@types/topojson-specification" "*"
+
+"@types/topojson-server@*":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/topojson-server/-/topojson-server-3.0.4.tgz#7a18bfa740d95ad3ce57a68f4e662fa8dc3965ca"
+ integrity sha512-5+ieK8ePfP+K2VH6Vgs1VCt+fO1U8XZHj0UsF+NktaF0DavAo1q3IvCBXgokk/xmtvoPltSUs6vxuR/zMdOE1g==
+ dependencies:
+ "@types/geojson" "*"
+ "@types/topojson-specification" "*"
+
+"@types/topojson-simplify@*":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/topojson-simplify/-/topojson-simplify-3.0.3.tgz#03013bde7b3eb47cac98b74ecb2a739927087415"
+ integrity sha512-sBO5UZ0O2dB0bNwo0vut2yLHhj3neUGi9uL7/ROdm8Gs6dtt4jcB9OGDKr+M2isZwQM2RuzVmifnMZpxj4IGNw==
+ dependencies:
+ "@types/geojson" "*"
+ "@types/topojson-specification" "*"
+
+"@types/topojson-specification@*", "@types/topojson-specification@^1.0.2":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@types/topojson-specification/-/topojson-specification-1.0.5.tgz#bf0009b2e0debb2d97237b124c00b9ea92570375"
+ integrity sha512-C7KvcQh+C2nr6Y2Ub4YfgvWvWCgP2nOQMtfhlnwsRL4pYmmwzBS7HclGiS87eQfDOU/DLQpX6GEscviaz4yLIQ==
+ dependencies:
+ "@types/geojson" "*"
+
+"@types/topojson@^3.2.3":
+ version "3.2.6"
+ resolved "https://registry.yarnpkg.com/@types/topojson/-/topojson-3.2.6.tgz#8ca07f8e71b0d6f978bc2b1f0a1c745adc0b52cd"
+ integrity sha512-ppfdlxjxofWJ66XdLgIlER/85RvpGyfOf8jrWf+3kVIjEatFxEZYD/Ea83jO672Xu1HRzd/ghwlbcZIUNHTskw==
+ dependencies:
+ "@types/geojson" "*"
+ "@types/topojson-client" "*"
+ "@types/topojson-server" "*"
+ "@types/topojson-simplify" "*"
+ "@types/topojson-specification" "*"
+
"@types/web-bluetooth@^0.0.21":
version "0.0.21"
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz#525433c784aed9b457aaa0ee3d92aeb71f346b63"
integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==
+"@unovis/dagre-layout@0.8.8-2":
+ version "0.8.8-2"
+ resolved "https://registry.yarnpkg.com/@unovis/dagre-layout/-/dagre-layout-0.8.8-2.tgz#0a4905dc062398927d11facbe1224da5311e070d"
+ integrity sha512-ZfDvfcYtzzhZhgKZty8XDi+zQIotfRqfNVF5M3dFQ9d9C5MTaRdbeBnPUkNrmlLJGgQ42HMOE2ajZLfm2VlRhg==
+ dependencies:
+ "@unovis/graphlibrary" "^2.2.0-2"
+ lodash-es "^4.17.21"
+
+"@unovis/graphlibrary@2.2.0-2", "@unovis/graphlibrary@^2.2.0-2":
+ version "2.2.0-2"
+ resolved "https://registry.yarnpkg.com/@unovis/graphlibrary/-/graphlibrary-2.2.0-2.tgz#fa18dead78745a94ea21f23aa4eeee95785954d1"
+ integrity sha512-HeEzpd/vDyWiIJt0rnh+2ICXUIuF2N0+Z9OJJiKg0DB+eFUcD+bk+9QPhYHwkFwfxdjDA9fHi1DZ/O/bbV58Nw==
+ dependencies:
+ lodash-es "^4.17.21"
+
+"@unovis/ts@^1.6.5":
+ version "1.6.5"
+ resolved "https://registry.yarnpkg.com/@unovis/ts/-/ts-1.6.5.tgz#69fb1ebf12bfabb7d0fb1f5024bf1b0fb8957959"
+ integrity sha512-4qeo0nKESrCX7vZr+zEGFjg2QbyjgxXN8//mPnvZi0e2mf+ZrjIRmHReHwO3u7fqMD7kgMMMD1ABJJFBcOempA==
+ dependencies:
+ "@emotion/css" "^11.7.1"
+ "@juggle/resize-observer" "^3.3.1"
+ "@types/d3" "^7.4.0"
+ "@types/d3-array" "~3.2.2"
+ "@types/d3-axis" "~3.0.6"
+ "@types/d3-brush" "~3.0.6"
+ "@types/d3-chord" "~3.0.6"
+ "@types/d3-collection" "^1.0.10"
+ "@types/d3-color" "~3.1.3"
+ "@types/d3-drag" "~3.0.7"
+ "@types/d3-ease" "~3.0.2"
+ "@types/d3-force" "~3.0.10"
+ "@types/d3-geo" "~3.1.0"
+ "@types/d3-hierarchy" "~3.1.7"
+ "@types/d3-interpolate" "~3.0.4"
+ "@types/d3-path" "~3.1.1"
+ "@types/d3-sankey" "^0.12.4"
+ "@types/d3-scale" "~4.0.9"
+ "@types/d3-selection" "~3.0.0"
+ "@types/d3-shape" "~3.1.7"
+ "@types/d3-timer" "~3.0.2"
+ "@types/d3-transition" "~3.0.9"
+ "@types/d3-zoom" "~3.0.8"
+ "@types/dagre" "^0.7.50"
+ "@types/geojson" "^7946.0.8"
+ "@types/leaflet" "1.7.6"
+ "@types/supercluster" "^5.0.2"
+ "@types/three" "^0.135.0"
+ "@types/throttle-debounce" "^5.0.0"
+ "@types/topojson" "^3.2.3"
+ "@types/topojson-client" "^3.0.0"
+ "@types/topojson-specification" "^1.0.2"
+ "@unovis/dagre-layout" "0.8.8-2"
+ "@unovis/graphlibrary" "2.2.0-2"
+ d3 "^7.2.1"
+ d3-array "~3"
+ d3-axis "~3"
+ d3-brush "~3"
+ d3-chord "~3"
+ d3-collection "^1.0.7"
+ d3-color "~3"
+ d3-drag "~3"
+ d3-ease "~3"
+ d3-force "~3"
+ d3-geo "~3"
+ d3-geo-projection "^4.0.0"
+ d3-hierarchy "~3"
+ d3-interpolate "~3"
+ d3-interpolate-path "^2.2.3"
+ d3-path "~3"
+ d3-sankey "^0.12.3"
+ d3-scale "~4"
+ d3-selection "~3"
+ d3-shape "~3"
+ d3-timer "~3"
+ d3-transition "~3"
+ d3-zoom "~3"
+ elkjs "^0.10.0"
+ geojson "^0.5.0"
+ leaflet "1.7.1"
+ maplibre-gl "^2.1.9"
+ striptags "^3.2.0"
+ supercluster "^7.1.5"
+ three "^0.135.0"
+ throttle-debounce "^5.0.0"
+ topojson-client "^3.1.0"
+ tslib "^2.3.1"
+ typescript "~4.2.4"
+
+"@unovis/vue@^1.6.5":
+ version "1.6.5"
+ resolved "https://registry.yarnpkg.com/@unovis/vue/-/vue-1.6.5.tgz#61f41c1ff378d173b7b2ac5297bcb86729e3fe67"
+ integrity sha512-BSyEeuenhSCqo0kox4xe3bWMDUn0gfF4haggrVhhNWcp6pDmctniDebOP/8AFKlhPhfGJ6b39MdvoQNhXwc46A==
+
"@uppy/companion-client@^5.1.1":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@uppy/companion-client/-/companion-client-5.1.1.tgz#5bfeda3312ba2b70e94d4fd73a31d0f7757884c0"
@@ -712,6 +1342,15 @@ aria-hidden@^1.2.4:
dependencies:
tslib "^2.0.0"
+babel-plugin-macros@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
+ integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ cosmiconfig "^7.0.0"
+ resolve "^1.19.0"
+
balanced-match@^4.0.2:
version "4.0.4"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a"
@@ -734,6 +1373,11 @@ buffer-from@^1.1.2:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
class-variance-authority@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787"
@@ -759,6 +1403,32 @@ combine-errors@^3.0.3:
custom-error-instance "2.1.1"
lodash.uniqby "4.5.0"
+commander@2:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@7:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+ integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+convert-source-map@^1.5.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
+ integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
+
+cosmiconfig@^7.0.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
+ integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
+ dependencies:
+ "@types/parse-json" "^4.0.0"
+ import-fresh "^3.2.1"
+ parse-json "^5.0.0"
+ path-type "^4.0.0"
+ yaml "^1.10.0"
+
cross-spawn@^7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
@@ -768,12 +1438,17 @@ cross-spawn@^7.0.6:
shebang-command "^2.0.0"
which "^2.0.1"
+csscolorparser@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b"
+ integrity sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==
+
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
-csstype@^3.2.3:
+csstype@^3.0.2, csstype@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
@@ -783,6 +1458,296 @@ custom-error-instance@2.1.1:
resolved "https://registry.yarnpkg.com/custom-error-instance/-/custom-error-instance-2.1.1.tgz#3cf6391487a6629a6247eb0ca0ce00081b7e361a"
integrity sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==
+"d3-array@1 - 2":
+ version "2.12.1"
+ resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81"
+ integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==
+ dependencies:
+ internmap "^1.0.0"
+
+"d3-array@1 - 3", "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0, d3-array@~3:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5"
+ integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
+ dependencies:
+ internmap "1 - 2"
+
+d3-axis@3, d3-axis@~3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322"
+ integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==
+
+d3-brush@3, d3-brush@~3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c"
+ integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-drag "2 - 3"
+ d3-interpolate "1 - 3"
+ d3-selection "3"
+ d3-transition "3"
+
+d3-chord@3, d3-chord@~3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966"
+ integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==
+ dependencies:
+ d3-path "1 - 3"
+
+d3-collection@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e"
+ integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==
+
+"d3-color@1 - 3", d3-color@3, d3-color@~3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
+ integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
+
+d3-contour@4:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-4.0.2.tgz#bb92063bc8c5663acb2422f99c73cbb6c6ae3bcc"
+ integrity sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==
+ dependencies:
+ d3-array "^3.2.0"
+
+d3-delaunay@6:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz#98169038733a0a5babbeda55054f795bb9e4a58b"
+ integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==
+ dependencies:
+ delaunator "5"
+
+"d3-dispatch@1 - 3", d3-dispatch@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
+ integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
+
+"d3-drag@2 - 3", d3-drag@3, d3-drag@~3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
+ integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-selection "3"
+
+"d3-dsv@1 - 3", d3-dsv@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73"
+ integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==
+ dependencies:
+ commander "7"
+ iconv-lite "0.6"
+ rw "1"
+
+"d3-ease@1 - 3", d3-ease@3, d3-ease@~3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
+ integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
+
+d3-fetch@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22"
+ integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==
+ dependencies:
+ d3-dsv "1 - 3"
+
+d3-force@3, d3-force@~3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4"
+ integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-quadtree "1 - 3"
+ d3-timer "1 - 3"
+
+"d3-format@1 - 3", d3-format@3:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.2.tgz#01fdb46b58beb1f55b10b42ad70b6e344d5eb2ae"
+ integrity sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==
+
+d3-geo-projection@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz#dc229e5ead78d31869a4e87cf1f45bd2716c48ca"
+ integrity sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==
+ dependencies:
+ commander "7"
+ d3-array "1 - 3"
+ d3-geo "1.12.0 - 3"
+
+"d3-geo@1.12.0 - 3", d3-geo@3, d3-geo@~3:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.1.1.tgz#6027cf51246f9b2ebd64f99e01dc7c3364033a4d"
+ integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==
+ dependencies:
+ d3-array "2.5.0 - 3"
+
+d3-hierarchy@3, d3-hierarchy@~3:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6"
+ integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==
+
+d3-interpolate-path@^2.2.3:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/d3-interpolate-path/-/d3-interpolate-path-2.3.0.tgz#ff919acb52968619f4b0dc5fa95653e581d5a0ca"
+ integrity sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ==
+
+"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3, d3-interpolate@~3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
+ integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
+ dependencies:
+ d3-color "1 - 3"
+
+d3-path@1:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf"
+ integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
+
+"d3-path@1 - 3", d3-path@3, d3-path@^3.1.0, d3-path@~3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526"
+ integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==
+
+d3-polygon@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398"
+ integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==
+
+"d3-quadtree@1 - 3", d3-quadtree@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f"
+ integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==
+
+d3-random@3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4"
+ integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==
+
+d3-sankey@^0.12.3:
+ version "0.12.3"
+ resolved "https://registry.yarnpkg.com/d3-sankey/-/d3-sankey-0.12.3.tgz#b3c268627bd72e5d80336e8de6acbfec9d15d01d"
+ integrity sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==
+ dependencies:
+ d3-array "1 - 2"
+ d3-shape "^1.2.0"
+
+d3-scale-chromatic@3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314"
+ integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==
+ dependencies:
+ d3-color "1 - 3"
+ d3-interpolate "1 - 3"
+
+d3-scale@4, d3-scale@~4:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
+ integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
+ dependencies:
+ d3-array "2.10.0 - 3"
+ d3-format "1 - 3"
+ d3-interpolate "1.2.0 - 3"
+ d3-time "2.1.1 - 3"
+ d3-time-format "2 - 4"
+
+"d3-selection@2 - 3", d3-selection@3, d3-selection@~3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
+ integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
+
+d3-shape@3, d3-shape@~3:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5"
+ integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==
+ dependencies:
+ d3-path "^3.1.0"
+
+d3-shape@^1.2.0:
+ version "1.3.7"
+ resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7"
+ integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
+ dependencies:
+ d3-path "1"
+
+"d3-time-format@2 - 4", d3-time-format@4:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a"
+ integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
+ dependencies:
+ d3-time "1 - 3"
+
+"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7"
+ integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
+ dependencies:
+ d3-array "2 - 3"
+
+"d3-timer@1 - 3", d3-timer@3, d3-timer@~3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
+ integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
+
+"d3-transition@2 - 3", d3-transition@3, d3-transition@~3:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
+ integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
+ dependencies:
+ d3-color "1 - 3"
+ d3-dispatch "1 - 3"
+ d3-ease "1 - 3"
+ d3-interpolate "1 - 3"
+ d3-timer "1 - 3"
+
+d3-zoom@3, d3-zoom@~3:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
+ integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-drag "2 - 3"
+ d3-interpolate "1 - 3"
+ d3-selection "2 - 3"
+ d3-transition "2 - 3"
+
+d3@^7.2.1:
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d"
+ integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==
+ dependencies:
+ d3-array "3"
+ d3-axis "3"
+ d3-brush "3"
+ d3-chord "3"
+ d3-color "3"
+ d3-contour "4"
+ d3-delaunay "6"
+ d3-dispatch "3"
+ d3-drag "3"
+ d3-dsv "3"
+ d3-ease "3"
+ d3-fetch "3"
+ d3-force "3"
+ d3-format "3"
+ d3-geo "3"
+ d3-hierarchy "3"
+ d3-interpolate "3"
+ d3-path "3"
+ d3-polygon "3"
+ d3-quadtree "3"
+ d3-random "3"
+ d3-scale "4"
+ d3-scale-chromatic "3"
+ d3-selection "3"
+ d3-shape "3"
+ d3-time "3"
+ d3-time-format "4"
+ d3-timer "3"
+ d3-transition "3"
+ d3-zoom "3"
+
debug@^4.3.1, debug@^4.3.2, debug@^4.4.0:
version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
@@ -800,6 +1765,13 @@ defu@^6.1.5:
resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.7.tgz#72543567c8e9f97ff13ce402b6dbe09ac5ae4d23"
integrity sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==
+delaunator@5:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.1.0.tgz#d13271fbf3aff6753f9ea6e235557f20901046ea"
+ integrity sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==
+ dependencies:
+ robust-predicates "^3.0.2"
+
detect-libc@^2.0.3:
version "2.1.2"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
@@ -809,6 +1781,16 @@ detect-libc@^2.0.3:
version "0.0.0"
uid ""
+earcut@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a"
+ integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==
+
+elkjs@^0.10.0:
+ version "0.10.2"
+ resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.10.2.tgz#7b95dbe5d41a5971b2eb13e54201855799389a43"
+ integrity sha512-Yx3ORtbAFrXelYkAy2g0eYyVY8QG0XEmGdQXmy0eithKKjbWRfl3Xe884lfkszfBF6UKyIy4LwfcZ3AZc8oxFw==
+
enhanced-resolve@5.21.6:
version "5.21.6"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz#aa207b43cf658e6ab3ba06896edc00c13c3127c6"
@@ -822,6 +1804,13 @@ entities@^7.0.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b"
integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==
+error-ex@^1.3.1:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414"
+ integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==
+ dependencies:
+ is-arrayish "^0.2.1"
+
error-stack-parser@^2.0.6:
version "2.1.4"
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
@@ -829,6 +1818,11 @@ error-stack-parser@^2.0.6:
dependencies:
stackframe "^1.3.4"
+es-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
@@ -967,6 +1961,11 @@ file-entry-cache@^8.0.0:
dependencies:
flat-cache "^4.0.0"
+find-root@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
+ integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
+
find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
@@ -993,6 +1992,31 @@ fsevents@~2.3.3:
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+function-bind@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+geojson-vt@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/geojson-vt/-/geojson-vt-3.2.1.tgz#f8adb614d2c1d3f6ee7c4265cad4bbf3ad60c8b7"
+ integrity sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==
+
+geojson@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0"
+ integrity sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==
+
+get-stream@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+
+gl-matrix@^3.4.3:
+ version "3.4.4"
+ resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.4.4.tgz#7789ee4982f62c7a7af447ee488f3bd6b0c77003"
+ integrity sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==
+
glob-parent@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
@@ -1000,6 +2024,15 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"
+global-prefix@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
+ integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
+ dependencies:
+ ini "^1.3.5"
+ kind-of "^6.0.2"
+ which "^1.3.1"
+
globals@^17.6.0:
version "17.6.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-17.6.0.tgz#0f0be018d5cca8690e6375ead1f65c4bb96191fc"
@@ -1010,16 +2043,70 @@ graceful-fs@^4.2.4:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+hasown@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003"
+ integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==
+ dependencies:
+ function-bind "^1.1.2"
+
+iconv-lite@0.6:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+ integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
+ieee754@^1.1.12:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
ignore@^5.2.0:
version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+import-fresh@^3.2.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
+ integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+ini@^1.3.5:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+ integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
+
+"internmap@1 - 2":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009"
+ integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
+
+internmap@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95"
+ integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-core-module@^2.16.1:
+ version "2.16.2"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.2.tgz#3e07450a8080ebce3fbf0cac494f4d2ab324e082"
+ integrity sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==
+ dependencies:
+ hasown "^2.0.3"
+
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -1057,11 +2144,26 @@ js-base64@^3.7.2:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.8.tgz#af44496bc09fa178ed9c4adf67eb2b46f5c6d2a4"
integrity sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==
+js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+jsesc@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
+ integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
+
json-buffer@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+json-parse-even-better-errors@^2.3.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -1072,6 +2174,11 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+kdbush@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0"
+ integrity sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==
+
keyv@^4.5.4:
version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
@@ -1079,6 +2186,16 @@ keyv@^4.5.4:
dependencies:
json-buffer "3.0.1"
+kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+leaflet@1.7.1:
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.7.1.tgz#10d684916edfe1bf41d688a3b97127c0322a2a19"
+ integrity sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==
+
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@@ -1161,6 +2278,11 @@ lightningcss@1.32.0, lightningcss@^1.32.0:
lightningcss-win32-arm64-msvc "1.32.0"
lightningcss-win32-x64-msvc "1.32.0"
+lines-and-columns@^1.1.6:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+ integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
linkifyjs@^4.3.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.3.3.tgz#da08f0eeb4d89a24541d09591fbdcc211eb8fef0"
@@ -1173,6 +2295,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash-es@^4.17.21:
+ version "4.18.1"
+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.18.1.tgz#b962eeb80d9d983a900bf342961fb7418ca10b1d"
+ integrity sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==
+
lodash._baseiteratee@~4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz#34a9b5543572727c3db2e78edae3c0e9e66bd102"
@@ -1235,6 +2362,36 @@ magic-string@^0.30.21:
dependencies:
"@jridgewell/sourcemap-codec" "^1.5.5"
+maplibre-gl@^2.1.9:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-2.4.0.tgz#2b53dbf526626bf4ee92ad4f33f13ef09e5af182"
+ integrity sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==
+ dependencies:
+ "@mapbox/geojson-rewind" "^0.5.2"
+ "@mapbox/jsonlint-lines-primitives" "^2.0.2"
+ "@mapbox/mapbox-gl-supported" "^2.0.1"
+ "@mapbox/point-geometry" "^0.1.0"
+ "@mapbox/tiny-sdf" "^2.0.5"
+ "@mapbox/unitbezier" "^0.0.1"
+ "@mapbox/vector-tile" "^1.3.1"
+ "@mapbox/whoots-js" "^3.1.0"
+ "@types/geojson" "^7946.0.10"
+ "@types/mapbox__point-geometry" "^0.1.2"
+ "@types/mapbox__vector-tile" "^1.3.0"
+ "@types/pbf" "^3.0.2"
+ csscolorparser "~1.0.3"
+ earcut "^2.2.4"
+ geojson-vt "^3.2.1"
+ gl-matrix "^3.4.3"
+ global-prefix "^3.0.0"
+ murmurhash-js "^1.0.0"
+ pbf "^3.2.1"
+ potpack "^1.0.2"
+ quickselect "^2.0.0"
+ supercluster "^7.1.5"
+ tinyqueue "^2.0.3"
+ vt-pbf "^3.1.3"
+
mime-match@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/mime-match/-/mime-match-1.0.2.tgz#3f87c31e9af1a5fd485fb9db134428b23bbb7ba8"
@@ -1249,6 +2406,11 @@ minimatch@^10.2.4:
dependencies:
brace-expansion "^5.0.5"
+minimist@^1.2.6:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
moment@^2.30.1:
version "2.30.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
@@ -1259,6 +2421,11 @@ ms@^2.1.3:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+murmurhash-js@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz#b06278e21fc6c37fa5313732b0412bcb6ae15f51"
+ integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==
+
namespace-emitter@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/namespace-emitter/-/namespace-emitter-2.0.1.tgz#978d51361c61313b4e6b8cf6f3853d08dfa2b17c"
@@ -1326,6 +2493,23 @@ p-retry@^6.1.0:
is-network-error "^1.0.0"
retry "^0.13.1"
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+parse-json@^5.0.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ error-ex "^1.3.1"
+ json-parse-even-better-errors "^2.3.0"
+ lines-and-columns "^1.1.6"
+
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@@ -1336,6 +2520,24 @@ path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-type@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+ integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+pbf@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.3.0.tgz#1790f3d99118333cc7f498de816028a346ef367f"
+ integrity sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==
+ dependencies:
+ ieee754 "^1.1.12"
+ resolve-protobuf-schema "^2.1.0"
+
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
@@ -1363,6 +2565,11 @@ postcss@^8.5.15:
picocolors "^1.1.1"
source-map-js "^1.2.1"
+potpack@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14"
+ integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==
+
preact@^10.26.10, preact@^10.5.13:
version "10.29.2"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.29.2.tgz#3e6069c471718b8d124d1cd67565114532e88d70"
@@ -1387,6 +2594,11 @@ proper-lockfile@^4.1.2:
retry "^0.12.0"
signal-exit "^3.0.2"
+protocol-buffers-schema@^3.3.1:
+ version "3.6.1"
+ resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz#fd9a58a5c4e96385b964808f3ddd58f9ef18c3c8"
+ integrity sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==
+
punycode@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
@@ -1397,6 +2609,11 @@ querystringify@^2.1.1:
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
+quickselect@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018"
+ integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==
+
reka-ui@^2.9.10:
version "2.9.10"
resolved "https://registry.yarnpkg.com/reka-ui/-/reka-ui-2.9.10.tgz#bc9766dfac7c81dd413098c3d712f0f8f68553ff"
@@ -1418,6 +2635,28 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-protobuf-schema@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz#9ca9a9e69cf192bbdaf1006ec1973948aa4a3758"
+ integrity sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==
+ dependencies:
+ protocol-buffers-schema "^3.3.1"
+
+resolve@^1.19.0:
+ version "1.22.12"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.12.tgz#f5b2a680897c69c238a13cd16b15671f8b73549f"
+ integrity sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==
+ dependencies:
+ es-errors "^1.3.0"
+ is-core-module "^2.16.1"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
@@ -1428,6 +2667,11 @@ retry@^0.13.1:
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
+robust-predicates@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.3.tgz#1099061b3349e2c5abec6c2ab0acd440d24d4062"
+ integrity sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==
+
rolldown@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.0.3.tgz#db88a3008fb0e28230a00423727ce75ba32121ac"
@@ -1452,6 +2696,16 @@ rolldown@1.0.3:
"@rolldown/binding-win32-arm64-msvc" "1.0.3"
"@rolldown/binding-win32-x64-msvc" "1.0.3"
+rw@1:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
+ integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==
+
+"safer-buffer@>= 2.1.2 < 3.0.0":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
semver@^7.6.3:
version "7.8.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.4.tgz#c73eceebae0616934be8dff28a7fd70757c8e696"
@@ -1484,6 +2738,11 @@ source-map@0.5.6:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==
+source-map@^0.5.7:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
+
stack-generator@^2.0.5:
version "2.0.10"
resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d"
@@ -1513,6 +2772,28 @@ stacktrace-js@^2.0.2:
stack-generator "^2.0.5"
stacktrace-gps "^3.0.4"
+striptags@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.2.0.tgz#cc74a137db2de8b0b9a370006334161f7dd67052"
+ integrity sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==
+
+stylis@4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51"
+ integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
+
+supercluster@^7.1.5:
+ version "7.1.5"
+ resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.5.tgz#65a6ce4a037a972767740614c19051b64b8be5a3"
+ integrity sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==
+ dependencies:
+ kdbush "^3.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
tailwind-merge@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-3.6.0.tgz#88d83242d1dd7bc847223f73dcf210dd1f2ee11c"
@@ -1528,6 +2809,16 @@ tapable@^2.3.3:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.3.tgz#5da7c9992c46038221267985ab28421a8879f160"
integrity sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==
+three@^0.135.0:
+ version "0.135.0"
+ resolved "https://registry.yarnpkg.com/three/-/three-0.135.0.tgz#cff4ec8edd3c51ce9bc7e88d13e8eafed4d0ddfc"
+ integrity sha512-kuEpuuxRzLv0MDsXai9huCxOSQPZ4vje6y0gn80SRmQvgz6/+rI0NAvCRAw56zYaWKMGMfqKWsxF9Qa2Z9xymQ==
+
+throttle-debounce@^5.0.0:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1"
+ integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==
+
tinyglobby@^0.2.17:
version "0.2.17"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.17.tgz#562a9a6c9eb2b3b123d39719f9af5bb44fcd7631"
@@ -1536,7 +2827,19 @@ tinyglobby@^0.2.17:
fdir "^6.5.0"
picomatch "^4.0.4"
-tslib@^2.0.0, tslib@^2.4.0, tslib@^2.8.0, tslib@^2.8.1:
+tinyqueue@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08"
+ integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==
+
+topojson-client@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/topojson-client/-/topojson-client-3.1.0.tgz#22e8b1ed08a2b922feeb4af6f53b6ef09a467b99"
+ integrity sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==
+ dependencies:
+ commander "2"
+
+tslib@^2.0.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.8.0, tslib@^2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
@@ -1566,6 +2869,11 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies:
prelude-ls "^1.2.1"
+typescript@~4.2.4:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
+ integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
+
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
@@ -1599,6 +2907,15 @@ vite@^8.0.16:
optionalDependencies:
fsevents "~2.3.3"
+vt-pbf@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac"
+ integrity sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==
+ dependencies:
+ "@mapbox/point-geometry" "0.1.0"
+ "@mapbox/vector-tile" "^1.3.1"
+ pbf "^3.2.1"
+
vue-demi@>=0.13.0:
version "0.14.10"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04"
@@ -1627,6 +2944,13 @@ vue@^3.5.38:
"@vue/server-renderer" "3.5.38"
"@vue/shared" "3.5.38"
+which@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
@@ -1649,6 +2973,11 @@ xml-name-validator@^4.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+yaml@^1.10.0:
+ version "1.10.3"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.3.tgz#76e407ed95c42684fb8e14641e5de62fe65bbcb3"
+ integrity sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==
+
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"