feat: polish
This commit is contained in:
parent
2e6177fe74
commit
4bd927bb4e
137 changed files with 6357 additions and 137560 deletions
|
|
@ -1,130 +1,113 @@
|
|||
<script lang="ts">
|
||||
import Map from "$lib/components/Map.svelte";
|
||||
import ControlPanel from "$lib/components/ControlPanel.svelte";
|
||||
import Navbar from "$lib/components/Navbar.svelte";
|
||||
import PanelContainer from "$lib/components/PanelContainer.svelte";
|
||||
import ScenarioPanel from "$lib/components/ScenarioPanel.svelte";
|
||||
import TabComponent from "$lib/components/ui/TabComponent.svelte";
|
||||
import PointEditor from "$lib/components/editors/PointEditor.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { PredictionStore } from "$lib/stores";
|
||||
import { addToast, removeToast } from "$lib/components/ui/Toast.svelte";
|
||||
import ToastContainer from '$lib/components/ui/Toast.svelte';
|
||||
import GenericPanel from "$lib/components/GenericPanel.svelte";
|
||||
import TimeLine from "$lib/components/TimeLine.svelte";
|
||||
import { onMount } from 'svelte';
|
||||
import { Map as MapView } from '$map';
|
||||
import type { IMap } from '$map';
|
||||
import { startCoordinateSelection } from '$map';
|
||||
import { Navbar } from '$features/auth';
|
||||
import { PanelContainer, TabBar } from '$ui';
|
||||
import { addToast, removeToast } from '$ui';
|
||||
import { ControlPanel, ScenarioPanel } from '$features/prediction';
|
||||
import {
|
||||
WorkspacesPanel,
|
||||
WorkspaceRenderer,
|
||||
workspacesStore,
|
||||
} from '$features/workspaces';
|
||||
import { SettingsPanel } from '$features/settings';
|
||||
import { TimeLine } from '$features/timeline';
|
||||
import { t } from '$i18n';
|
||||
import { requireAuthenticated } from '$auth';
|
||||
|
||||
let map: Map | null = null;
|
||||
let panelContainer: PanelContainer | null = null;
|
||||
let controlPanel: ControlPanel | null = null;
|
||||
let selectionToastId: string | null = null;
|
||||
let activeTabLeft: 'control' | 'scenario' | 'about' = 'scenario';
|
||||
let activeTabRight: 'layers' | 'settings' | 'results' = 'results';
|
||||
type LeftTab = 'scenario' | 'conditions' | 'about';
|
||||
type RightTab = 'results' | 'workspaces' | 'settings';
|
||||
|
||||
onMount(() => {
|
||||
PredictionStore.subscribe((data) => {
|
||||
if (data) {
|
||||
map?.clearMapLayers();
|
||||
}
|
||||
});
|
||||
console.log("ControlPanel mounted");
|
||||
console.log(panelContainer);
|
||||
let mapComponent = $state<MapView | null>(null);
|
||||
let controlPanelRef = $state<ControlPanel | null>(null);
|
||||
let selectionToastId: string | null = null;
|
||||
let selectionDispose: (() => void) | null = null;
|
||||
|
||||
if (panelContainer) {
|
||||
let element = panelContainer.getElement();
|
||||
if (!element) return;
|
||||
let leftTab = $state<LeftTab>('scenario');
|
||||
let rightTab = $state<RightTab>('workspaces');
|
||||
|
||||
// Disable click and scroll propagation to prevent map interaction
|
||||
element.addEventListener('click', (e) => e.stopPropagation());
|
||||
element.addEventListener('dblclick', (e) => e.stopPropagation());
|
||||
element.addEventListener('mousedown', (e) => e.stopPropagation());
|
||||
element.addEventListener('touchstart', (e) => e.stopPropagation());
|
||||
element.addEventListener('wheel', (e) => e.stopPropagation());
|
||||
}
|
||||
});
|
||||
onMount(async () => {
|
||||
const ok = await requireAuthenticated('/login');
|
||||
if (!ok) return;
|
||||
if ($workspacesStore.items.length === 0) {
|
||||
workspacesStore.add({ name: $t('workspaces.defaultName', { n: 1 }) });
|
||||
}
|
||||
});
|
||||
|
||||
function handleClickSelectOnMap() {
|
||||
if (map) {
|
||||
map.startSelection();
|
||||
console.log("Selection mode enabled");
|
||||
if (!selectionToastId) {
|
||||
selectionToastId = addToast({
|
||||
header: "Режим выбора координат",
|
||||
body: "Кликните на карту, чтобы выбрать координаты",
|
||||
color: "info",
|
||||
persistent: true,
|
||||
onRemoveCallback: () => {
|
||||
selectionToastId = null;
|
||||
map?.stopSelection();
|
||||
console.log("Selection mode disabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleMapReady(_map: IMap) {
|
||||
// Map is ready; consumers inside <MapView /> already have access via context.
|
||||
}
|
||||
|
||||
function handleCoordinateSelection(event: CustomEvent<{ lat: number; lng: number }>) {
|
||||
const { lat, lng } = event.detail;
|
||||
controlPanel?.updateLaunchPosition(lat, lng);
|
||||
console.log(`Selected coordinates: ${lat}, ${lng}`);
|
||||
if (selectionToastId) {
|
||||
removeToast(selectionToastId);
|
||||
selectionToastId = null;
|
||||
}
|
||||
}
|
||||
function handleSelectOnMap() {
|
||||
const map = mapComponent?.getInstance();
|
||||
if (!map) return;
|
||||
|
||||
function handleTimeUpdate(event: CustomEvent<{ index: number; lat: number; lng: number; alt: number; datetime: Date }>) {
|
||||
const { lat, lng } = event.detail;
|
||||
map?.updateAnimatedMarker(lat, lng);
|
||||
}
|
||||
selectionDispose = startCoordinateSelection(map, ({ lat, lng }) => {
|
||||
controlPanelRef?.updateLaunchPosition(lat, lng);
|
||||
if (selectionToastId) {
|
||||
removeToast(selectionToastId);
|
||||
selectionToastId = null;
|
||||
}
|
||||
selectionDispose = null;
|
||||
});
|
||||
|
||||
selectionToastId = addToast({
|
||||
header: $t('selection.header'),
|
||||
body: $t('selection.body'),
|
||||
color: 'info',
|
||||
persistent: true,
|
||||
onRemove: () => {
|
||||
selectionDispose?.();
|
||||
selectionDispose = null;
|
||||
selectionToastId = null;
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<Navbar />
|
||||
<div style="height: var(--navbar-height);"></div> <!-- Spacer for fixed navbar -->
|
||||
<Map bind:this={map} mode="prediction" bind:data={$PredictionStore} on:coordinatesSelected={handleCoordinateSelection}>
|
||||
<PanelContainer bind:this={panelContainer} position="left">
|
||||
<TabComponent
|
||||
tabs={[
|
||||
{ id: 'scenario', icon: 'file-earmark-play', label: 'Сценарий' },
|
||||
{ id: 'control', icon: 'sliders', label: 'Условия' },
|
||||
{ id: 'about', icon: 'info-circle', label: 'О проекте' }
|
||||
]}
|
||||
bind:activeTab={activeTabLeft}
|
||||
/>
|
||||
|
||||
<div>
|
||||
{#if activeTabLeft === 'control'}
|
||||
<ControlPanel onSelectOnMapClick={handleClickSelectOnMap} bind:this={controlPanel} />
|
||||
{:else if activeTabLeft === 'scenario'}
|
||||
<ScenarioPanel />
|
||||
{:else if activeTabLeft === 'about'}
|
||||
<!-- <AboutPanel /> -->
|
||||
{/if}
|
||||
</div>
|
||||
</PanelContainer>
|
||||
<PanelContainer position="right">
|
||||
<TabComponent
|
||||
justify="end"
|
||||
tabs={[
|
||||
{ id: 'results', icon: 'bar-chart-line', label: 'Результаты' },
|
||||
{ id: 'layers', icon: 'layers', label: 'Слои' },
|
||||
{ id: 'settings', icon: 'gear', label: 'Настройки' },
|
||||
]}
|
||||
bind:activeTab={activeTabRight}
|
||||
/>
|
||||
|
||||
<div>
|
||||
{#if activeTabRight === 'results'}
|
||||
<GenericPanel />
|
||||
{:else if activeTabRight === 'layers'}
|
||||
<GenericPanel />
|
||||
{/if}
|
||||
</div>
|
||||
</PanelContainer>
|
||||
<ToastContainer />
|
||||
{#if $PredictionStore}
|
||||
<TimeLine prediction={$PredictionStore} on:timeUpdate={handleTimeUpdate} />
|
||||
{/if}
|
||||
</Map>
|
||||
<Navbar />
|
||||
<div style="height: var(--navbar-height);"></div>
|
||||
<MapView bind:this={mapComponent} onReady={handleMapReady}>
|
||||
<WorkspaceRenderer />
|
||||
|
||||
<PanelContainer position="left">
|
||||
<TabBar
|
||||
tabs={[
|
||||
{ id: 'scenario', icon: 'file-earmark-play', label: $t('panel.scenario') },
|
||||
{ id: 'conditions', icon: 'sliders', label: $t('panel.conditions') },
|
||||
{ id: 'about', icon: 'info-circle', label: $t('panel.about') },
|
||||
]}
|
||||
bind:active={leftTab} />
|
||||
<div>
|
||||
{#if leftTab === 'scenario'}
|
||||
<ScenarioPanel />
|
||||
{:else if leftTab === 'conditions'}
|
||||
<ControlPanel bind:this={controlPanelRef} onSelectOnMapClick={handleSelectOnMap} />
|
||||
{/if}
|
||||
</div>
|
||||
</PanelContainer>
|
||||
|
||||
<PanelContainer position="right">
|
||||
<TabBar
|
||||
justify="end"
|
||||
tabs={[
|
||||
{ id: 'workspaces', icon: 'layers', label: $t('panel.workspaces') },
|
||||
{ id: 'results', icon: 'bar-chart-line', label: $t('panel.results') },
|
||||
{ id: 'settings', icon: 'gear', label: $t('panel.settings') },
|
||||
]}
|
||||
bind:active={rightTab} />
|
||||
<div>
|
||||
{#if rightTab === 'workspaces'}
|
||||
<WorkspacesPanel />
|
||||
{:else if rightTab === 'settings'}
|
||||
<SettingsPanel />
|
||||
{/if}
|
||||
</div>
|
||||
</PanelContainer>
|
||||
|
||||
<TimeLine />
|
||||
</MapView>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
export const ssr =false;
|
||||
Loading…
Add table
Add a link
Reference in a new issue