@smartnet360/svelte-components 0.0.142 → 0.0.143
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-tools/components/AntennaControls.svelte +14 -6
- package/dist/apps/antenna-tools/components/AntennaTools.svelte +6 -5
- package/dist/core/Auth/auth.svelte.js +72 -39
- package/dist/core/Auth/config.d.ts +1 -0
- package/dist/core/Auth/config.js +3 -4
- package/dist/map-v3/demo/DemoMap.svelte +3 -1
- package/dist/map-v3/features/custom/components/CustomCellSetManager.svelte +105 -10
- package/dist/map-v3/features/custom/components/CustomCellSetManager.svelte.d.ts +3 -0
- package/dist/map-v3/features/custom/components/ServerSetBrowser.svelte +398 -0
- package/dist/map-v3/features/custom/components/ServerSetBrowser.svelte.d.ts +22 -0
- package/dist/map-v3/features/custom/components/index.d.ts +1 -0
- package/dist/map-v3/features/custom/components/index.js +1 -0
- package/dist/map-v3/features/custom/db/custom-sets-api.d.ts +65 -0
- package/dist/map-v3/features/custom/db/custom-sets-api.js +220 -0
- package/dist/map-v3/features/custom/db/custom-sets-repository.d.ts +77 -0
- package/dist/map-v3/features/custom/db/custom-sets-repository.js +195 -0
- package/dist/map-v3/features/custom/db/index.d.ts +10 -0
- package/dist/map-v3/features/custom/db/index.js +9 -0
- package/dist/map-v3/features/custom/db/schema.sql +102 -0
- package/dist/map-v3/features/custom/db/types.d.ts +95 -0
- package/dist/map-v3/features/custom/db/types.js +95 -0
- package/dist/map-v3/features/custom/index.d.ts +2 -0
- package/dist/map-v3/features/custom/index.js +2 -0
- package/dist/map-v3/features/custom/logic/csv-parser.js +8 -3
- package/package.json +1 -1
|
@@ -67,6 +67,10 @@
|
|
|
67
67
|
// Avoid overwriting user selections
|
|
68
68
|
if (selectedAntenna !== internalSelectedAntenna) {
|
|
69
69
|
internalSelectedAntenna = selectedAntenna;
|
|
70
|
+
// Also sync frequency when antenna is set externally
|
|
71
|
+
if (selectedAntenna && selectedAntenna.frequency) {
|
|
72
|
+
selectedFrequency = selectedAntenna.frequency;
|
|
73
|
+
}
|
|
70
74
|
}
|
|
71
75
|
});
|
|
72
76
|
|
|
@@ -76,12 +80,13 @@
|
|
|
76
80
|
let newTilts: string[] = ['0'];
|
|
77
81
|
|
|
78
82
|
if (internalSelectedAntenna) {
|
|
79
|
-
// Find
|
|
83
|
+
// Find antennas with the same name
|
|
80
84
|
let sameName = antennas.filter(a => a.name === internalSelectedAntenna!.name);
|
|
81
85
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
// Always filter by frequency - use selectedFrequency or fall back to antenna's frequency
|
|
87
|
+
const freqToUse = selectedFrequency ?? internalSelectedAntenna.frequency;
|
|
88
|
+
if (freqToUse) {
|
|
89
|
+
sameName = sameName.filter(a => a.frequency === freqToUse);
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
const allTilts = new Set<string>();
|
|
@@ -114,12 +119,15 @@
|
|
|
114
119
|
// Handle case where availableElectricalTilts might not be populated yet
|
|
115
120
|
const targetTilt = availableElectricalTilts[tiltIndex] || '0';
|
|
116
121
|
|
|
122
|
+
// Always use frequency filter - selectedFrequency or the current antenna's frequency
|
|
123
|
+
const freqToUse = selectedFrequency ?? internalSelectedAntenna?.frequency;
|
|
124
|
+
|
|
117
125
|
// Find antenna with matching name, frequency, and electrical tilt
|
|
118
126
|
return antennas.find(antenna => {
|
|
119
127
|
if (antenna.name !== antennaName) return false;
|
|
120
128
|
|
|
121
|
-
//
|
|
122
|
-
if (
|
|
129
|
+
// Must match frequency
|
|
130
|
+
if (freqToUse && antenna.frequency !== freqToUse) return false;
|
|
123
131
|
|
|
124
132
|
if (antenna.tilt) {
|
|
125
133
|
const tiltString = antenna.tilt.toString();
|
|
@@ -197,10 +197,10 @@
|
|
|
197
197
|
|
|
198
198
|
// === Helpers ===
|
|
199
199
|
function updateAvailableTilts(antenna: Antenna) {
|
|
200
|
-
// Collect
|
|
201
|
-
const
|
|
200
|
+
// Collect available tilts from antennas with the same name AND frequency
|
|
201
|
+
const sameNameAndFreq = antennas.filter(a => a.name === antenna.name && a.frequency === antenna.frequency);
|
|
202
202
|
const allTilts = new Set<string>();
|
|
203
|
-
|
|
203
|
+
sameNameAndFreq.forEach(a => {
|
|
204
204
|
if (a.tilt) {
|
|
205
205
|
const tiltStr = a.tilt.toString();
|
|
206
206
|
if (tiltStr.includes(',')) {
|
|
@@ -214,9 +214,10 @@
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
function getAvailableTiltsForAntenna(antenna: Antenna): string[] {
|
|
217
|
-
|
|
217
|
+
// Collect available tilts from antennas with the same name AND frequency
|
|
218
|
+
const sameNameAndFreq = antennas.filter(a => a.name === antenna.name && a.frequency === antenna.frequency);
|
|
218
219
|
const allTilts = new Set<string>();
|
|
219
|
-
|
|
220
|
+
sameNameAndFreq.forEach(a => {
|
|
220
221
|
if (a.tilt) {
|
|
221
222
|
const tiltStr = a.tilt.toString();
|
|
222
223
|
if (tiltStr.includes(',')) {
|
|
@@ -2,30 +2,61 @@
|
|
|
2
2
|
import { browser } from '$app/environment';
|
|
3
3
|
// Demo mode - enabled via PUBLIC_DEMO_MODE env var (GitHub Pages)
|
|
4
4
|
const DEMO_MODE = import.meta.env.PUBLIC_DEMO_MODE === 'true';
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
// Demo user
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
// Test users configuration (no backend required)
|
|
6
|
+
const TEST_USERS = {
|
|
7
|
+
// Demo user (view-only permissions)
|
|
8
|
+
demo: {
|
|
9
|
+
password: 'demo123',
|
|
10
|
+
session: {
|
|
11
|
+
user: {
|
|
12
|
+
id: 'demo-user',
|
|
13
|
+
username: 'demo',
|
|
14
|
+
displayName: 'Demo User',
|
|
15
|
+
email: 'demo@example.com',
|
|
16
|
+
groups: ['demo'],
|
|
17
|
+
loginTime: Date.now()
|
|
18
|
+
},
|
|
19
|
+
permissions: [
|
|
20
|
+
{ featureId: 'map-view', canView: true, canEdit: false, canAdmin: false },
|
|
21
|
+
{ featureId: 'coverage-view', canView: true, canEdit: false, canAdmin: false },
|
|
22
|
+
{ featureId: 'charts-view', canView: true, canEdit: false, canAdmin: false },
|
|
23
|
+
{ featureId: 'desktop-view', canView: true, canEdit: false, canAdmin: false },
|
|
24
|
+
{ featureId: 'table-view', canView: true, canEdit: false, canAdmin: false },
|
|
25
|
+
{ featureId: 'antenna-view', canView: true, canEdit: false, canAdmin: false },
|
|
26
|
+
{ featureId: 'site-check', canView: true, canEdit: false, canAdmin: false },
|
|
27
|
+
{ featureId: 'settings-view', canView: true, canEdit: false, canAdmin: false }
|
|
28
|
+
],
|
|
29
|
+
token: 'demo-token',
|
|
30
|
+
expiresAt: Date.now() + (24 * 60 * 60 * 1000) // 24 hours
|
|
31
|
+
}
|
|
16
32
|
},
|
|
17
|
-
permissions
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
// Test user (full permissions, for testing without backend)
|
|
34
|
+
test: {
|
|
35
|
+
password: 'test',
|
|
36
|
+
session: {
|
|
37
|
+
user: {
|
|
38
|
+
id: 'test-user',
|
|
39
|
+
username: 'test',
|
|
40
|
+
displayName: 'Test User',
|
|
41
|
+
email: 'test@localhost',
|
|
42
|
+
groups: ['testers', 'admins'],
|
|
43
|
+
loginTime: Date.now()
|
|
44
|
+
},
|
|
45
|
+
permissions: [
|
|
46
|
+
{ featureId: 'map-view', canView: true, canEdit: true, canAdmin: true },
|
|
47
|
+
{ featureId: 'coverage-view', canView: true, canEdit: true, canAdmin: true },
|
|
48
|
+
{ featureId: 'charts-view', canView: true, canEdit: true, canAdmin: true },
|
|
49
|
+
{ featureId: 'desktop-view', canView: true, canEdit: true, canAdmin: true },
|
|
50
|
+
{ featureId: 'table-view', canView: true, canEdit: true, canAdmin: true },
|
|
51
|
+
{ featureId: 'antenna-view', canView: true, canEdit: true, canAdmin: true },
|
|
52
|
+
{ featureId: 'site-check', canView: true, canEdit: true, canAdmin: true },
|
|
53
|
+
{ featureId: 'settings-view', canView: true, canEdit: true, canAdmin: true },
|
|
54
|
+
{ featureId: 'admin', canView: true, canEdit: true, canAdmin: true }
|
|
55
|
+
],
|
|
56
|
+
token: 'test-token',
|
|
57
|
+
expiresAt: Date.now() + (24 * 60 * 60 * 1000) // 24 hours
|
|
58
|
+
}
|
|
59
|
+
}
|
|
29
60
|
};
|
|
30
61
|
// Default configuration
|
|
31
62
|
const defaultConfig = {
|
|
@@ -107,22 +138,22 @@ export function createAuthState(config = {}) {
|
|
|
107
138
|
isLoading = true;
|
|
108
139
|
error = null;
|
|
109
140
|
try {
|
|
110
|
-
//
|
|
141
|
+
// Check for test users (works in demo mode or as fallback)
|
|
142
|
+
const testUser = TEST_USERS[username.toLowerCase()];
|
|
143
|
+
if (testUser && password === testUser.password) {
|
|
144
|
+
// Create fresh session with updated timestamp
|
|
145
|
+
const testSession = {
|
|
146
|
+
...testUser.session,
|
|
147
|
+
user: { ...testUser.session.user, loginTime: Date.now() },
|
|
148
|
+
expiresAt: Date.now() + (24 * 60 * 60 * 1000)
|
|
149
|
+
};
|
|
150
|
+
saveSession(testSession);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
// In demo mode, only allow test users
|
|
111
154
|
if (DEMO_MODE) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const demoSession = {
|
|
115
|
-
...DEMO_SESSION,
|
|
116
|
-
user: { ...DEMO_SESSION.user, loginTime: Date.now() },
|
|
117
|
-
expiresAt: Date.now() + (24 * 60 * 60 * 1000)
|
|
118
|
-
};
|
|
119
|
-
saveSession(demoSession);
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
error = 'Demo mode: Use username "demo" and password "demo123"';
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
155
|
+
error = 'Demo mode: Use "demo/demo123" or "test/test" to login';
|
|
156
|
+
return false;
|
|
126
157
|
}
|
|
127
158
|
// Normal mode: call server API
|
|
128
159
|
const response = await fetch(`${cfg.apiEndpoint}/login`, {
|
|
@@ -141,7 +172,9 @@ export function createAuthState(config = {}) {
|
|
|
141
172
|
}
|
|
142
173
|
}
|
|
143
174
|
catch (e) {
|
|
144
|
-
|
|
175
|
+
// If server is unavailable, show helpful message about test users
|
|
176
|
+
const networkError = e instanceof Error ? e.message : 'Network error';
|
|
177
|
+
error = `${networkError}. Try "test/test" for testing without a backend.`;
|
|
145
178
|
return false;
|
|
146
179
|
}
|
|
147
180
|
finally {
|
|
@@ -21,5 +21,6 @@ export declare const DEFAULT_DEV_PERMISSIONS: FeatureAccess[];
|
|
|
21
21
|
export declare function getDevConfig(): DevConfig;
|
|
22
22
|
/**
|
|
23
23
|
* Check if we're in development mode
|
|
24
|
+
* Currently disabled - authentication is always required
|
|
24
25
|
*/
|
|
25
26
|
export declare function isDevMode(): boolean;
|
package/dist/core/Auth/config.js
CHANGED
|
@@ -246,11 +246,10 @@ export function getDevConfig() {
|
|
|
246
246
|
}
|
|
247
247
|
/**
|
|
248
248
|
* Check if we're in development mode
|
|
249
|
+
* Currently disabled - authentication is always required
|
|
249
250
|
*/
|
|
250
251
|
export function isDevMode() {
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
return true;
|
|
254
|
-
}
|
|
252
|
+
// Auto-login disabled - always require authentication
|
|
253
|
+
// Use test/test or demo/demo123 for testing without a backend
|
|
255
254
|
return false;
|
|
256
255
|
}
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
import {
|
|
25
25
|
CustomCellsLayer,
|
|
26
26
|
CustomCellSetManager,
|
|
27
|
-
createCustomCellSetsStore
|
|
27
|
+
createCustomCellSetsStore,
|
|
28
|
+
createCustomSetsApi
|
|
28
29
|
} from '../features/custom';
|
|
29
30
|
// Custom Sites Feature
|
|
30
31
|
import {
|
|
@@ -103,6 +104,7 @@
|
|
|
103
104
|
<CustomCellSetManager
|
|
104
105
|
position="top-left"
|
|
105
106
|
setsStore={customCellSets}
|
|
107
|
+
apiClient={createCustomSetsApi('/api/custom-sets')}
|
|
106
108
|
/>
|
|
107
109
|
|
|
108
110
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - Quick Add (paste cell names)
|
|
8
8
|
* - Global size/opacity sliders
|
|
9
9
|
* - Collapsible set sections with TreeView + color pickers
|
|
10
|
+
* - Server sharing (optional, when apiClient provided)
|
|
10
11
|
*/
|
|
11
12
|
import { untrack } from 'svelte';
|
|
12
13
|
import { MapControl } from '../../../shared';
|
|
@@ -14,17 +15,22 @@
|
|
|
14
15
|
import type { CustomCellSetsStore } from '../stores/custom-cell-sets.svelte';
|
|
15
16
|
import type { CustomCellImportResult, CustomCellSet } from '../types';
|
|
16
17
|
import { buildCustomCellTree } from '../logic/tree-adapter';
|
|
18
|
+
import type { CustomSetsApiClient } from '../db/custom-sets-api';
|
|
19
|
+
import ServerSetBrowser from './ServerSetBrowser.svelte';
|
|
17
20
|
|
|
18
21
|
interface Props {
|
|
19
22
|
/** The custom cell sets store */
|
|
20
23
|
setsStore: CustomCellSetsStore;
|
|
21
24
|
/** Control position on map */
|
|
22
25
|
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
26
|
+
/** Optional API client for server sharing */
|
|
27
|
+
apiClient?: CustomSetsApiClient;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
let {
|
|
26
31
|
setsStore,
|
|
27
|
-
position = 'top-left'
|
|
32
|
+
position = 'top-left',
|
|
33
|
+
apiClient
|
|
28
34
|
}: Props = $props();
|
|
29
35
|
|
|
30
36
|
// Global display settings - initialized from first set if available
|
|
@@ -57,6 +63,11 @@
|
|
|
57
63
|
let quickAddName = $state('Quick Selection');
|
|
58
64
|
let quickAddResult = $state<CustomCellImportResult | null>(null);
|
|
59
65
|
|
|
66
|
+
// Server sharing state
|
|
67
|
+
let showServerBrowser = $state(false);
|
|
68
|
+
let savingSetId = $state<string | null>(null);
|
|
69
|
+
let saveError = $state('');
|
|
70
|
+
|
|
60
71
|
// Track expanded sets
|
|
61
72
|
let expandedSets = $state<Set<string>>(new Set());
|
|
62
73
|
|
|
@@ -223,6 +234,26 @@
|
|
|
223
234
|
}
|
|
224
235
|
}
|
|
225
236
|
|
|
237
|
+
// Server sharing functions
|
|
238
|
+
async function saveSetToServer(set: CustomCellSet) {
|
|
239
|
+
if (!apiClient) return;
|
|
240
|
+
|
|
241
|
+
savingSetId = set.id;
|
|
242
|
+
saveError = '';
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const result = await apiClient.saveSet(set);
|
|
246
|
+
|
|
247
|
+
if (!result.success) {
|
|
248
|
+
saveError = result.error || 'Failed to save to server';
|
|
249
|
+
}
|
|
250
|
+
} catch (err) {
|
|
251
|
+
saveError = err instanceof Error ? err.message : 'Failed to save';
|
|
252
|
+
} finally {
|
|
253
|
+
savingSetId = null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
226
257
|
function toggleExpanded(setId: string) {
|
|
227
258
|
if (expandedSets.has(setId)) {
|
|
228
259
|
expandedSets.delete(setId);
|
|
@@ -252,9 +283,20 @@
|
|
|
252
283
|
|
|
253
284
|
<MapControl {position} title="Custom Layers" icon="layers" controlWidth="320px">
|
|
254
285
|
{#snippet actions()}
|
|
286
|
+
{#if apiClient}
|
|
287
|
+
<button
|
|
288
|
+
class="btn btn-sm btn-outline-primary border-0 p-1 px-2"
|
|
289
|
+
title="Load from Server"
|
|
290
|
+
aria-label="Load from Server"
|
|
291
|
+
onclick={() => showServerBrowser = true}
|
|
292
|
+
>
|
|
293
|
+
<i class="bi bi-cloud-download"></i>
|
|
294
|
+
</button>
|
|
295
|
+
{/if}
|
|
255
296
|
<button
|
|
256
297
|
class="btn btn-sm btn-outline-primary border-0 p-1 px-2"
|
|
257
298
|
title="Quick Add (paste cell names)"
|
|
299
|
+
aria-label="Quick Add"
|
|
258
300
|
onclick={() => showQuickAdd = true}
|
|
259
301
|
>
|
|
260
302
|
<i class="bi bi-pencil-square"></i>
|
|
@@ -262,6 +304,7 @@
|
|
|
262
304
|
<button
|
|
263
305
|
class="btn btn-sm btn-outline-primary border-0 p-1 px-2"
|
|
264
306
|
title="Import CSV"
|
|
307
|
+
aria-label="Import CSV"
|
|
265
308
|
onclick={triggerFileInput}
|
|
266
309
|
>
|
|
267
310
|
<i class="bi bi-upload"></i>
|
|
@@ -290,6 +333,22 @@
|
|
|
290
333
|
</div>
|
|
291
334
|
{/if}
|
|
292
335
|
|
|
336
|
+
<!-- Server save error -->
|
|
337
|
+
{#if saveError}
|
|
338
|
+
<div class="alert alert-warning py-1 px-2 mb-2 small d-flex justify-content-between align-items-center">
|
|
339
|
+
<span>
|
|
340
|
+
<i class="bi bi-cloud-slash me-1"></i>
|
|
341
|
+
{saveError}
|
|
342
|
+
</span>
|
|
343
|
+
<button
|
|
344
|
+
type="button"
|
|
345
|
+
class="btn-close btn-sm"
|
|
346
|
+
aria-label="Dismiss"
|
|
347
|
+
onclick={() => saveError = ''}
|
|
348
|
+
></button>
|
|
349
|
+
</div>
|
|
350
|
+
{/if}
|
|
351
|
+
|
|
293
352
|
<!-- Empty state -->
|
|
294
353
|
{#if setsStore.sets.length === 0}
|
|
295
354
|
<div class="text-center p-3">
|
|
@@ -297,7 +356,15 @@
|
|
|
297
356
|
<p class="text-muted small mt-2 mb-0">
|
|
298
357
|
No custom layers loaded.
|
|
299
358
|
</p>
|
|
300
|
-
<div class="d-flex gap-2 justify-content-center mt-2">
|
|
359
|
+
<!-- <div class="d-flex gap-2 justify-content-center mt-2 flex-wrap">
|
|
360
|
+
{#if apiClient}
|
|
361
|
+
<button
|
|
362
|
+
class="btn btn-sm btn-outline-success"
|
|
363
|
+
onclick={() => showServerBrowser = true}
|
|
364
|
+
>
|
|
365
|
+
<i class="bi bi-cloud-download me-1"></i> Load from Server
|
|
366
|
+
</button>
|
|
367
|
+
{/if}
|
|
301
368
|
<button
|
|
302
369
|
class="btn btn-sm btn-outline-primary"
|
|
303
370
|
onclick={() => showQuickAdd = true}
|
|
@@ -310,7 +377,7 @@
|
|
|
310
377
|
>
|
|
311
378
|
<i class="bi bi-upload me-1"></i> Import CSV
|
|
312
379
|
</button>
|
|
313
|
-
</div>
|
|
380
|
+
</div> -->
|
|
314
381
|
</div>
|
|
315
382
|
{:else}
|
|
316
383
|
<!-- Global Controls -->
|
|
@@ -390,13 +457,31 @@
|
|
|
390
457
|
<small class="text-muted">{getSetItemCounts(set)} · {set.groups.length} groups</small>
|
|
391
458
|
</div>
|
|
392
459
|
</div>
|
|
393
|
-
<
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
460
|
+
<div class="d-flex gap-1">
|
|
461
|
+
{#if apiClient}
|
|
462
|
+
<button
|
|
463
|
+
class="btn btn-sm btn-outline-primary border-0 p-1"
|
|
464
|
+
title="Share to Server"
|
|
465
|
+
aria-label="Share to Server"
|
|
466
|
+
onclick={() => saveSetToServer(set)}
|
|
467
|
+
disabled={savingSetId === set.id}
|
|
468
|
+
>
|
|
469
|
+
{#if savingSetId === set.id}
|
|
470
|
+
<span class="spinner-border spinner-border-sm" role="status"></span>
|
|
471
|
+
{:else}
|
|
472
|
+
<i class="bi bi-cloud-upload"></i>
|
|
473
|
+
{/if}
|
|
474
|
+
</button>
|
|
475
|
+
{/if}
|
|
476
|
+
<button
|
|
477
|
+
class="btn btn-sm btn-outline-danger border-0 p-1"
|
|
478
|
+
title="Remove"
|
|
479
|
+
aria-label="Remove set"
|
|
480
|
+
onclick={() => removeSet(set.id)}
|
|
481
|
+
>
|
|
482
|
+
<i class="bi bi-trash"></i>
|
|
483
|
+
</button>
|
|
484
|
+
</div>
|
|
400
485
|
</div>
|
|
401
486
|
|
|
402
487
|
<!-- Set Content (TreeView) -->
|
|
@@ -635,6 +720,16 @@
|
|
|
635
720
|
</div>
|
|
636
721
|
{/if}
|
|
637
722
|
|
|
723
|
+
<!-- Server Set Browser Modal -->
|
|
724
|
+
{#if apiClient}
|
|
725
|
+
<ServerSetBrowser
|
|
726
|
+
{apiClient}
|
|
727
|
+
{setsStore}
|
|
728
|
+
show={showServerBrowser}
|
|
729
|
+
onclose={() => showServerBrowser = false}
|
|
730
|
+
/>
|
|
731
|
+
{/if}
|
|
732
|
+
|
|
638
733
|
<style>
|
|
639
734
|
.custom-layers-manager {
|
|
640
735
|
font-size: 0.875rem;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { CustomCellSetsStore } from '../stores/custom-cell-sets.svelte';
|
|
2
|
+
import type { CustomSetsApiClient } from '../db/custom-sets-api';
|
|
2
3
|
interface Props {
|
|
3
4
|
/** The custom cell sets store */
|
|
4
5
|
setsStore: CustomCellSetsStore;
|
|
5
6
|
/** Control position on map */
|
|
6
7
|
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
8
|
+
/** Optional API client for server sharing */
|
|
9
|
+
apiClient?: CustomSetsApiClient;
|
|
7
10
|
}
|
|
8
11
|
declare const CustomCellSetManager: import("svelte").Component<Props, {}, "">;
|
|
9
12
|
type CustomCellSetManager = ReturnType<typeof CustomCellSetManager>;
|