@smartnet360/svelte-components 0.0.40 → 0.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte +0 -5
- package/dist/apps/antenna-pattern/components/DbNotification.svelte +1 -1
- package/dist/apps/antenna-pattern/components/chart-engines/PolarBarChart.svelte +2 -0
- package/dist/apps/antenna-pattern/components/chart-engines/PolarBarChart.svelte.d.ts +1 -0
- package/dist/apps/site-check/helper.js +3 -0
- package/dist/core/Charts/ChartCard.svelte +9 -12
- package/dist/core/Charts/ChartCard.svelte.d.ts +5 -21
- package/dist/core/Charts/ChartComponent.svelte +2 -2
- package/dist/core/Charts/editor/ChartLayoutEditor.svelte +8 -8
- package/dist/core/Charts/editor/GridPreview.svelte +0 -3
- package/dist/core/Charts/editor/KPIPicker.svelte +6 -7
- package/dist/core/Charts/editor/KPIPicker.svelte.d.ts +4 -20
- package/dist/core/Charts/editor/PropertiesPanel.svelte +6 -3
- package/dist/core/Charts/editor/PropertiesPanel.svelte.d.ts +8 -18
- package/dist/core/Map/Map.svelte +312 -0
- package/dist/core/Map/Map.svelte.d.ts +230 -0
- package/dist/core/Map/index.d.ts +9 -0
- package/dist/core/Map/index.js +9 -0
- package/dist/core/Map/mapSettings.d.ts +147 -0
- package/dist/core/Map/mapSettings.js +226 -0
- package/dist/core/Map/mapStore.d.ts +73 -0
- package/dist/core/Map/mapStore.js +136 -0
- package/dist/core/Map/types.d.ts +72 -0
- package/dist/core/Map/types.js +32 -0
- package/dist/core/Settings/FieldRenderer.svelte +19 -15
- package/dist/core/Settings/FieldRenderer.svelte.d.ts +12 -25
- package/dist/core/Settings/Settings.svelte +48 -29
- package/dist/core/Settings/Settings.svelte.d.ts +26 -20
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/package.json +15 -11
|
@@ -139,11 +139,6 @@
|
|
|
139
139
|
ant1MechanicalTilt = 0;
|
|
140
140
|
ant2ElectricalTilt = 0;
|
|
141
141
|
ant2MechanicalTilt = 0;
|
|
142
|
-
|
|
143
|
-
// Update chart if initialized
|
|
144
|
-
if (chartInitialized) {
|
|
145
|
-
await updateChart();
|
|
146
|
-
}
|
|
147
142
|
}
|
|
148
143
|
|
|
149
144
|
console.log('Data refreshed from settings modal');
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
let visible = false;
|
|
7
7
|
let message = '';
|
|
8
8
|
let type: 'success' | 'error' | 'info' = 'info';
|
|
9
|
-
let timer:
|
|
9
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
10
10
|
|
|
11
11
|
const unsubscribe = dataOperationStatus.subscribe(status => {
|
|
12
12
|
if (!status.operation) return;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
selectedAntenna2?: Antenna | null;
|
|
16
16
|
viewMode?: 'single' | 'compare';
|
|
17
17
|
patternType?: 'horizontal' | 'vertical';
|
|
18
|
+
patternDisplayMode?: 'normalized' | 'gain-adjusted';
|
|
18
19
|
ant1ElectricalTilt?: number;
|
|
19
20
|
ant1MechanicalTilt?: number;
|
|
20
21
|
ant2ElectricalTilt?: number;
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
selectedAntenna2 = null,
|
|
28
29
|
viewMode = 'single',
|
|
29
30
|
patternType = 'vertical',
|
|
31
|
+
patternDisplayMode = 'normalized',
|
|
30
32
|
ant1ElectricalTilt = 0,
|
|
31
33
|
ant1MechanicalTilt = 0,
|
|
32
34
|
ant2ElectricalTilt = 0,
|
|
@@ -4,6 +4,7 @@ interface Props {
|
|
|
4
4
|
selectedAntenna2?: Antenna | null;
|
|
5
5
|
viewMode?: 'single' | 'compare';
|
|
6
6
|
patternType?: 'horizontal' | 'vertical';
|
|
7
|
+
patternDisplayMode?: 'normalized' | 'gain-adjusted';
|
|
7
8
|
ant1ElectricalTilt?: number;
|
|
8
9
|
ant1MechanicalTilt?: number;
|
|
9
10
|
ant2ElectricalTilt?: number;
|
|
@@ -21,6 +21,9 @@ export function expandLayoutForCells(baseLayout, data, stylingConfig) {
|
|
|
21
21
|
// Deep clone the layout structure and expand KPIs
|
|
22
22
|
const expandedLayout = {
|
|
23
23
|
layoutName: baseLayout.layoutName,
|
|
24
|
+
hoverMode: baseLayout.hoverMode, // Preserve hover mode from base layout
|
|
25
|
+
coloredHover: baseLayout.coloredHover, // Preserve colored hover setting
|
|
26
|
+
movingAverage: baseLayout.movingAverage, // Preserve moving average config
|
|
24
27
|
sections: baseLayout.sections.map((section) => ({
|
|
25
28
|
...section,
|
|
26
29
|
charts: section.charts.map((chart) => ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<svelte:options runes={true} />
|
|
2
2
|
|
|
3
3
|
<script lang="ts">
|
|
4
|
-
import { onMount
|
|
4
|
+
import { onMount } from 'svelte';
|
|
5
5
|
import Plotly from 'plotly.js-dist-min';
|
|
6
6
|
import type { Chart as ChartModel, ChartMarker, MovingAverageConfig, HoverMode } from './charts.model.js';
|
|
7
7
|
import { createTimeSeriesTraceWithMA, getYAxisTitle, createDefaultPlotlyLayout } from './data-utils.js';
|
|
@@ -9,15 +9,6 @@
|
|
|
9
9
|
import { getKPIValues, type ProcessedChartData } from './data-processor.js';
|
|
10
10
|
import { log } from '../logger';
|
|
11
11
|
|
|
12
|
-
const dispatch = createEventDispatcher<{
|
|
13
|
-
chartcontextmenu: {
|
|
14
|
-
chart: ChartModel;
|
|
15
|
-
sectionId?: string;
|
|
16
|
-
clientX: number;
|
|
17
|
-
clientY: number;
|
|
18
|
-
};
|
|
19
|
-
}>();
|
|
20
|
-
|
|
21
12
|
interface Props {
|
|
22
13
|
chart: ChartModel;
|
|
23
14
|
processedData: ProcessedChartData; // Pre-processed KPI values and timestamps
|
|
@@ -33,9 +24,15 @@
|
|
|
33
24
|
runtimeShowOriginal?: boolean; // Runtime control for showing original lines
|
|
34
25
|
runtimeShowMarkers?: boolean; // Runtime control for showing markers (default: true)
|
|
35
26
|
runtimeShowLegend?: boolean; // Runtime control for showing legend (default: true)
|
|
27
|
+
onchartcontextmenu?: (detail: {
|
|
28
|
+
chart: ChartModel;
|
|
29
|
+
sectionId?: string;
|
|
30
|
+
clientX: number;
|
|
31
|
+
clientY: number;
|
|
32
|
+
}) => void;
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
let { chart, processedData, markers, plotlyLayout, enableAdaptation = true, sectionId, sectionMovingAverage, layoutMovingAverage, layoutHoverMode, layoutColoredHover = true, runtimeMAOverride, runtimeShowOriginal, runtimeShowMarkers = true, runtimeShowLegend = true }: Props = $props();
|
|
35
|
+
let { chart, processedData, markers, plotlyLayout, enableAdaptation = true, sectionId, sectionMovingAverage, layoutMovingAverage, layoutHoverMode, layoutColoredHover = true, runtimeMAOverride, runtimeShowOriginal, runtimeShowMarkers = true, runtimeShowLegend = true, onchartcontextmenu }: Props = $props();
|
|
39
36
|
|
|
40
37
|
// Chart container div and state
|
|
41
38
|
let chartDiv: HTMLElement;
|
|
@@ -44,7 +41,7 @@
|
|
|
44
41
|
|
|
45
42
|
function handleContextMenu(event: MouseEvent) {
|
|
46
43
|
event.preventDefault();
|
|
47
|
-
|
|
44
|
+
onchartcontextmenu?.({
|
|
48
45
|
chart,
|
|
49
46
|
sectionId,
|
|
50
47
|
clientX: event.clientX,
|
|
@@ -15,29 +15,13 @@ interface Props {
|
|
|
15
15
|
runtimeShowOriginal?: boolean;
|
|
16
16
|
runtimeShowMarkers?: boolean;
|
|
17
17
|
runtimeShowLegend?: boolean;
|
|
18
|
-
|
|
19
|
-
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
20
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
21
|
-
$$bindings?: Bindings;
|
|
22
|
-
} & Exports;
|
|
23
|
-
(internal: unknown, props: Props & {
|
|
24
|
-
$$events?: Events;
|
|
25
|
-
$$slots?: Slots;
|
|
26
|
-
}): Exports & {
|
|
27
|
-
$set?: any;
|
|
28
|
-
$on?: any;
|
|
29
|
-
};
|
|
30
|
-
z_$$bindings?: Bindings;
|
|
31
|
-
}
|
|
32
|
-
declare const ChartCard: $$__sveltets_2_IsomorphicComponent<Props, {
|
|
33
|
-
chartcontextmenu: CustomEvent<{
|
|
18
|
+
onchartcontextmenu?: (detail: {
|
|
34
19
|
chart: ChartModel;
|
|
35
20
|
sectionId?: string;
|
|
36
21
|
clientX: number;
|
|
37
22
|
clientY: number;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
type ChartCard = InstanceType<typeof ChartCard>;
|
|
23
|
+
}) => void;
|
|
24
|
+
}
|
|
25
|
+
declare const ChartCard: import("svelte").Component<Props, {}, "">;
|
|
26
|
+
type ChartCard = ReturnType<typeof ChartCard>;
|
|
43
27
|
export default ChartCard;
|
|
@@ -333,7 +333,7 @@
|
|
|
333
333
|
runtimeShowOriginal={globalControls.movingAverage?.showOriginal}
|
|
334
334
|
runtimeShowMarkers={globalControls.markers?.enabled}
|
|
335
335
|
runtimeShowLegend={globalControls.legend?.enabled}
|
|
336
|
-
|
|
336
|
+
onchartcontextmenu={(detail) => handleChartContextMenu(detail, section)}
|
|
337
337
|
/>
|
|
338
338
|
</div>
|
|
339
339
|
{/each}
|
|
@@ -475,7 +475,7 @@
|
|
|
475
475
|
runtimeShowOriginal={globalControls.movingAverage?.showOriginal}
|
|
476
476
|
runtimeShowMarkers={globalControls.markers?.enabled}
|
|
477
477
|
runtimeShowLegend={globalControls.legend?.enabled}
|
|
478
|
-
|
|
478
|
+
onchartcontextmenu={(detail) => handleChartContextMenu(detail, activeZoom.section)}
|
|
479
479
|
/>
|
|
480
480
|
</div>
|
|
481
481
|
</div>
|
|
@@ -73,18 +73,18 @@
|
|
|
73
73
|
input.click();
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
function handleOpenKPIPicker(
|
|
77
|
-
kpiPickerContext =
|
|
76
|
+
function handleOpenKPIPicker(detail: { sectionId: string; chartIndex: number; side: 'yLeft' | 'yRight' }) {
|
|
77
|
+
kpiPickerContext = detail;
|
|
78
78
|
showKPIPicker = true;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function handleKPISelected(
|
|
81
|
+
function handleKPISelected(kpi: KPI) {
|
|
82
82
|
if (kpiPickerContext) {
|
|
83
83
|
editorStore.addKPI(
|
|
84
84
|
kpiPickerContext.sectionId,
|
|
85
85
|
kpiPickerContext.chartIndex,
|
|
86
86
|
kpiPickerContext.side,
|
|
87
|
-
|
|
87
|
+
kpi
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
90
|
showKPIPicker = false;
|
|
@@ -172,12 +172,12 @@
|
|
|
172
172
|
|
|
173
173
|
<!-- Center Panel: Grid Preview -->
|
|
174
174
|
<div class="panel-center flex-grow-1 bg-light overflow-auto">
|
|
175
|
-
<GridPreview
|
|
175
|
+
<GridPreview />
|
|
176
176
|
</div>
|
|
177
177
|
|
|
178
178
|
<!-- Right Panel: Properties -->
|
|
179
179
|
<div class="panel-right border-start bg-white overflow-auto" style="width: 320px; min-width: 320px;">
|
|
180
|
-
<PropertiesPanel
|
|
180
|
+
<PropertiesPanel onopenkpipicker={handleOpenKPIPicker} />
|
|
181
181
|
</div>
|
|
182
182
|
</div>
|
|
183
183
|
{/if}
|
|
@@ -188,8 +188,8 @@
|
|
|
188
188
|
<KPIPicker
|
|
189
189
|
show={showKPIPicker}
|
|
190
190
|
{availableKPIs}
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
onselect={handleKPISelected}
|
|
192
|
+
onclose={() => { showKPIPicker = false; kpiPickerContext = null; }}
|
|
193
193
|
/>
|
|
194
194
|
{/if}
|
|
195
195
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
<svelte:options runes={true} />
|
|
2
2
|
|
|
3
3
|
<script lang="ts">
|
|
4
|
-
import { createEventDispatcher } from 'svelte';
|
|
5
4
|
import { editorStore, currentLayout, selection } from './editorState.js';
|
|
6
5
|
import type { ChartGrid } from '../charts.model.js';
|
|
7
6
|
|
|
8
|
-
const dispatch = createEventDispatcher();
|
|
9
|
-
|
|
10
7
|
function getGridDimensions(grid: ChartGrid): { rows: number; cols: number } {
|
|
11
8
|
const map: Record<ChartGrid, { rows: number; cols: number }> = {
|
|
12
9
|
'2x2': { rows: 2, cols: 2 },
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
<svelte:options runes={true} />
|
|
2
2
|
|
|
3
3
|
<script lang="ts">
|
|
4
|
-
import { createEventDispatcher } from 'svelte';
|
|
5
4
|
import type { KPI } from '../charts.model.js';
|
|
6
5
|
|
|
7
6
|
interface Props {
|
|
8
7
|
show: boolean;
|
|
9
8
|
availableKPIs: KPI[];
|
|
9
|
+
onselect?: (kpi: KPI) => void;
|
|
10
|
+
onclose?: () => void;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
let { show, availableKPIs }: Props = $props();
|
|
13
|
-
|
|
14
|
-
const dispatch = createEventDispatcher();
|
|
13
|
+
let { show, availableKPIs, onselect, onclose }: Props = $props();
|
|
15
14
|
|
|
16
15
|
let searchQuery = $state('');
|
|
17
16
|
let selectedKPI = $state<KPI | null>(null);
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
|
|
37
36
|
function handleConfirm() {
|
|
38
37
|
if (selectedKPI) {
|
|
39
|
-
|
|
38
|
+
onselect?.(selectedKPI);
|
|
40
39
|
handleClose();
|
|
41
40
|
}
|
|
42
41
|
}
|
|
@@ -44,7 +43,7 @@
|
|
|
44
43
|
function handleClose() {
|
|
45
44
|
searchQuery = '';
|
|
46
45
|
selectedKPI = null;
|
|
47
|
-
|
|
46
|
+
onclose?.();
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
function handleKeydown(e: KeyboardEvent) {
|
|
@@ -56,7 +55,7 @@
|
|
|
56
55
|
}
|
|
57
56
|
</script>
|
|
58
57
|
|
|
59
|
-
<svelte:window
|
|
58
|
+
<svelte:window onkeydown={handleKeydown} />
|
|
60
59
|
|
|
61
60
|
{#if show}
|
|
62
61
|
<!-- Bootstrap Modal -->
|
|
@@ -2,25 +2,9 @@ import type { KPI } from '../charts.model.js';
|
|
|
2
2
|
interface Props {
|
|
3
3
|
show: boolean;
|
|
4
4
|
availableKPIs: KPI[];
|
|
5
|
+
onselect?: (kpi: KPI) => void;
|
|
6
|
+
onclose?: () => void;
|
|
5
7
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
$$bindings?: Bindings;
|
|
9
|
-
} & Exports;
|
|
10
|
-
(internal: unknown, props: Props & {
|
|
11
|
-
$$events?: Events;
|
|
12
|
-
$$slots?: Slots;
|
|
13
|
-
}): Exports & {
|
|
14
|
-
$set?: any;
|
|
15
|
-
$on?: any;
|
|
16
|
-
};
|
|
17
|
-
z_$$bindings?: Bindings;
|
|
18
|
-
}
|
|
19
|
-
declare const KpiPicker: $$__sveltets_2_IsomorphicComponent<Props, {
|
|
20
|
-
select: CustomEvent<any>;
|
|
21
|
-
close: CustomEvent<any>;
|
|
22
|
-
} & {
|
|
23
|
-
[evt: string]: CustomEvent<any>;
|
|
24
|
-
}, {}, {}, "">;
|
|
25
|
-
type KpiPicker = InstanceType<typeof KpiPicker>;
|
|
8
|
+
declare const KpiPicker: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type KpiPicker = ReturnType<typeof KpiPicker>;
|
|
26
10
|
export default KpiPicker;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
<svelte:options runes={true} />
|
|
2
2
|
|
|
3
3
|
<script lang="ts">
|
|
4
|
-
import { createEventDispatcher } from 'svelte';
|
|
5
4
|
import { editorStore, currentLayout, selection, selectedItem } from './editorState.js';
|
|
6
5
|
import type { ChartGrid, Section, Chart } from '../charts.model.js';
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
interface Props {
|
|
8
|
+
onopenkpipicker?: (detail: { sectionId: string; chartIndex: number; side: 'yLeft' | 'yRight' }) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { onopenkpipicker }: Props = $props();
|
|
9
12
|
|
|
10
13
|
const gridOptions: ChartGrid[] = ['2x2', '3x3', '4x4', '1x2', '1x4', '1x8'];
|
|
11
14
|
|
|
12
15
|
function handleOpenKPIPicker(side: 'yLeft' | 'yRight') {
|
|
13
16
|
if ($selection.type === 'chart' && $selection.sectionId && $selection.chartIndex !== undefined) {
|
|
14
|
-
|
|
17
|
+
onopenkpipicker?.({
|
|
15
18
|
sectionId: $selection.sectionId,
|
|
16
19
|
chartIndex: $selection.chartIndex,
|
|
17
20
|
side
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
interface
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
$$slots?: Slots;
|
|
8
|
-
}): Exports & {
|
|
9
|
-
$set?: any;
|
|
10
|
-
$on?: any;
|
|
11
|
-
};
|
|
12
|
-
z_$$bindings?: Bindings;
|
|
1
|
+
interface Props {
|
|
2
|
+
onopenkpipicker?: (detail: {
|
|
3
|
+
sectionId: string;
|
|
4
|
+
chartIndex: number;
|
|
5
|
+
side: 'yLeft' | 'yRight';
|
|
6
|
+
}) => void;
|
|
13
7
|
}
|
|
14
|
-
declare const PropertiesPanel:
|
|
15
|
-
|
|
16
|
-
} & {
|
|
17
|
-
[evt: string]: CustomEvent<any>;
|
|
18
|
-
}, {}, {}, "">;
|
|
19
|
-
type PropertiesPanel = InstanceType<typeof PropertiesPanel>;
|
|
8
|
+
declare const PropertiesPanel: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type PropertiesPanel = ReturnType<typeof PropertiesPanel>;
|
|
20
10
|
export default PropertiesPanel;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import mapboxgl from 'mapbox-gl';
|
|
4
|
+
import { MapboxOverlay } from '@deck.gl/mapbox';
|
|
5
|
+
import type { Layer } from '@deck.gl/core';
|
|
6
|
+
import { createMapStore, type MapStore } from './mapStore';
|
|
7
|
+
import { createSettingsStore } from '../Settings/store';
|
|
8
|
+
import { mapSettingsSchema } from './mapSettings';
|
|
9
|
+
import { MAP_STYLES, DEFAULT_MAP_OPTIONS, type MapOptions } from './types';
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
accessToken: string;
|
|
13
|
+
initialOptions?: Partial<MapOptions>;
|
|
14
|
+
namespace?: string;
|
|
15
|
+
layers?: Layer[];
|
|
16
|
+
mapStore?: MapStore;
|
|
17
|
+
onMapLoaded?: (map: mapboxgl.Map) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
accessToken,
|
|
22
|
+
initialOptions = {},
|
|
23
|
+
namespace = 'map',
|
|
24
|
+
layers = [],
|
|
25
|
+
mapStore = $bindable(),
|
|
26
|
+
onMapLoaded
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
// Container element
|
|
30
|
+
let container = $state<HTMLDivElement>();
|
|
31
|
+
let map = $state<mapboxgl.Map>();
|
|
32
|
+
let overlay = $state<MapboxOverlay>();
|
|
33
|
+
let styleLoaded = $state(false);
|
|
34
|
+
|
|
35
|
+
// Create stores if not provided
|
|
36
|
+
if (!mapStore) {
|
|
37
|
+
mapStore = createMapStore();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Create settings store
|
|
41
|
+
const settings = createSettingsStore(mapSettingsSchema, namespace);
|
|
42
|
+
|
|
43
|
+
// Reactive: Update layers when prop changes
|
|
44
|
+
$effect(() => {
|
|
45
|
+
if (mapStore && layers.length > 0) {
|
|
46
|
+
mapStore.setLayers(layers);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Reactive: Update map style when settings change
|
|
51
|
+
$effect(() => {
|
|
52
|
+
if (!map || !styleLoaded || !$settings.appearance.style) return;
|
|
53
|
+
|
|
54
|
+
const styleUrl = MAP_STYLES[$settings.appearance.style as keyof typeof MAP_STYLES];
|
|
55
|
+
if (styleUrl && map.getStyle()?.name !== styleUrl) {
|
|
56
|
+
styleLoaded = false; // Mark as not loaded during transition
|
|
57
|
+
map.setStyle(styleUrl);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Reactive: Update interaction handlers
|
|
62
|
+
$effect(() => {
|
|
63
|
+
const currentMap = map;
|
|
64
|
+
if (!currentMap) return;
|
|
65
|
+
|
|
66
|
+
const handlers = [
|
|
67
|
+
{ name: 'dragRotate', value: $settings.interaction.dragRotate },
|
|
68
|
+
{ name: 'touchZoomRotate', value: $settings.interaction.touchZoomRotate },
|
|
69
|
+
{ name: 'scrollZoom', value: $settings.interaction.scrollZoom },
|
|
70
|
+
{ name: 'doubleClickZoom', value: $settings.interaction.doubleClickZoom },
|
|
71
|
+
{ name: 'dragPan', value: $settings.interaction.dragPan }
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
handlers.forEach(({ name, value }) => {
|
|
75
|
+
const handler = currentMap[name as keyof mapboxgl.Map] as any;
|
|
76
|
+
if (handler) {
|
|
77
|
+
value ? handler.enable() : handler.disable();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Reactive: Update pitch and bearing
|
|
83
|
+
$effect(() => {
|
|
84
|
+
if (!map || !styleLoaded) return;
|
|
85
|
+
|
|
86
|
+
map.setPitch($settings.view.pitch as number);
|
|
87
|
+
map.setBearing($settings.view.bearing as number);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Reactive: Toggle 3D buildings
|
|
91
|
+
$effect(() => {
|
|
92
|
+
if (!map || !styleLoaded) return;
|
|
93
|
+
|
|
94
|
+
if ($settings.appearance.show3dBuildings) {
|
|
95
|
+
add3dBuildings();
|
|
96
|
+
} else if (map.getLayer('3d-buildings')) {
|
|
97
|
+
map.removeLayer('3d-buildings');
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Reactive: Toggle terrain
|
|
102
|
+
$effect(() => {
|
|
103
|
+
if (!map || !styleLoaded) return;
|
|
104
|
+
|
|
105
|
+
if ($settings.appearance.showTerrain) {
|
|
106
|
+
addTerrain();
|
|
107
|
+
} else if (map.getTerrain()) {
|
|
108
|
+
map.setTerrain(null);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// One-time initialization using onMount (recommended for async/DOM-dependent setup)
|
|
113
|
+
onMount(() => {
|
|
114
|
+
if (!container) return;
|
|
115
|
+
|
|
116
|
+
// Set Mapbox access token
|
|
117
|
+
mapboxgl.accessToken = accessToken;
|
|
118
|
+
|
|
119
|
+
// Merge default options with initial options and settings
|
|
120
|
+
const options: MapOptions = {
|
|
121
|
+
...DEFAULT_MAP_OPTIONS,
|
|
122
|
+
...initialOptions,
|
|
123
|
+
accessToken
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Initialize Mapbox map
|
|
127
|
+
map = new mapboxgl.Map({
|
|
128
|
+
container,
|
|
129
|
+
style: MAP_STYLES[$settings.appearance.style as keyof typeof MAP_STYLES] || options.style,
|
|
130
|
+
center: options.center,
|
|
131
|
+
zoom: options.zoom,
|
|
132
|
+
pitch: $settings.view.pitch as number,
|
|
133
|
+
bearing: $settings.view.bearing as number,
|
|
134
|
+
minZoom: $settings.view.minZoom as number,
|
|
135
|
+
maxZoom: $settings.view.maxZoom as number,
|
|
136
|
+
antialias: $settings.performance.antialias as boolean,
|
|
137
|
+
maxTileCacheSize: $settings.performance.maxTileCacheSize as number,
|
|
138
|
+
fadeDuration: $settings.performance.fadeDuration as number
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Add controls based on settings
|
|
142
|
+
if ($settings.controls.showNavigationControls) {
|
|
143
|
+
map.addControl(new mapboxgl.NavigationControl(), 'top-right');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if ($settings.controls.showScaleControl) {
|
|
147
|
+
map.addControl(new mapboxgl.ScaleControl(), 'bottom-left');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if ($settings.controls.showFullscreenControl) {
|
|
151
|
+
map.addControl(new mapboxgl.FullscreenControl(), 'top-right');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if ($settings.controls.showGeolocateControl) {
|
|
155
|
+
map.addControl(
|
|
156
|
+
new mapboxgl.GeolocateControl({
|
|
157
|
+
positionOptions: { enableHighAccuracy: true },
|
|
158
|
+
trackUserLocation: true,
|
|
159
|
+
showUserHeading: true
|
|
160
|
+
}),
|
|
161
|
+
'top-right'
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Initialize Deck.GL overlay
|
|
166
|
+
overlay = new MapboxOverlay({
|
|
167
|
+
interleaved: true,
|
|
168
|
+
layers: layers
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
map.addControl(overlay as any);
|
|
172
|
+
|
|
173
|
+
// Store map and overlay instances
|
|
174
|
+
if (mapStore) {
|
|
175
|
+
mapStore.setMap(map);
|
|
176
|
+
mapStore.setOverlay(overlay);
|
|
177
|
+
mapStore.setLayers(layers);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Map load event
|
|
181
|
+
map.on('load', () => {
|
|
182
|
+
if (!map) return;
|
|
183
|
+
|
|
184
|
+
styleLoaded = true;
|
|
185
|
+
|
|
186
|
+
if (mapStore) {
|
|
187
|
+
mapStore.setLoaded(true);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Add 3D buildings if enabled
|
|
191
|
+
if ($settings.appearance.show3dBuildings) {
|
|
192
|
+
add3dBuildings();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Add terrain if enabled
|
|
196
|
+
if ($settings.appearance.showTerrain) {
|
|
197
|
+
addTerrain();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Call user callback
|
|
201
|
+
if (onMapLoaded) {
|
|
202
|
+
onMapLoaded(map);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Track style loading for dynamic style changes
|
|
207
|
+
map.on('style.load', () => {
|
|
208
|
+
styleLoaded = true;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Track view state changes
|
|
212
|
+
map.on('move', () => {
|
|
213
|
+
if (!map || !mapStore) return;
|
|
214
|
+
|
|
215
|
+
mapStore.updateViewState({
|
|
216
|
+
center: map.getCenter().toArray() as [number, number],
|
|
217
|
+
zoom: map.getZoom(),
|
|
218
|
+
pitch: map.getPitch(),
|
|
219
|
+
bearing: map.getBearing()
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Cleanup on destroy
|
|
224
|
+
return () => {
|
|
225
|
+
if (map) {
|
|
226
|
+
map.remove();
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
function add3dBuildings() {
|
|
232
|
+
if (!map) return;
|
|
233
|
+
if (!map.getLayer('3d-buildings')) {
|
|
234
|
+
const mapLayers = map.getStyle().layers;
|
|
235
|
+
const labelLayerId = mapLayers?.find(
|
|
236
|
+
(layer) => layer.type === 'symbol' && layer.layout?.['text-field']
|
|
237
|
+
)?.id;
|
|
238
|
+
|
|
239
|
+
map.addLayer(
|
|
240
|
+
{
|
|
241
|
+
id: '3d-buildings',
|
|
242
|
+
source: 'composite',
|
|
243
|
+
'source-layer': 'building',
|
|
244
|
+
filter: ['==', 'extrude', 'true'],
|
|
245
|
+
type: 'fill-extrusion',
|
|
246
|
+
minzoom: 15,
|
|
247
|
+
paint: {
|
|
248
|
+
'fill-extrusion-color': '#aaa',
|
|
249
|
+
'fill-extrusion-height': [
|
|
250
|
+
'interpolate',
|
|
251
|
+
['linear'],
|
|
252
|
+
['zoom'],
|
|
253
|
+
15,
|
|
254
|
+
0,
|
|
255
|
+
15.05,
|
|
256
|
+
['get', 'height']
|
|
257
|
+
],
|
|
258
|
+
'fill-extrusion-base': [
|
|
259
|
+
'interpolate',
|
|
260
|
+
['linear'],
|
|
261
|
+
['zoom'],
|
|
262
|
+
15,
|
|
263
|
+
0,
|
|
264
|
+
15.05,
|
|
265
|
+
['get', 'min_height']
|
|
266
|
+
],
|
|
267
|
+
'fill-extrusion-opacity': 0.6
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
labelLayerId
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function addTerrain() {
|
|
276
|
+
if (!map) return;
|
|
277
|
+
if (!map.getSource('mapbox-dem')) {
|
|
278
|
+
map.addSource('mapbox-dem', {
|
|
279
|
+
type: 'raster-dem',
|
|
280
|
+
url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
|
|
281
|
+
tileSize: 512,
|
|
282
|
+
maxzoom: 14
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
map.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Export settings store for external access
|
|
290
|
+
export { settings };
|
|
291
|
+
</script>
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
<div class="map-container">
|
|
296
|
+
<div bind:this={container} class="map"></div>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<style>
|
|
300
|
+
.map-container {
|
|
301
|
+
width: 100%;
|
|
302
|
+
height: 100%;
|
|
303
|
+
position: relative;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.map {
|
|
307
|
+
width: 100%;
|
|
308
|
+
height: 100%;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* Import Mapbox CSS - This should be in your app's global CSS */
|
|
312
|
+
</style>
|